• 我的2016年终总结(PF项目框架设计心得分享 2.0rc)


    在无数的日夜里,熬出了多少的黑眼圈,致勤勤恳恳工作的各位朋友与自己。每到了年末的时候总想写的什么,主要是为了回顾以往一年里到底做了什么,这便是年终总结的主要意义。在此我将要总结的是和我在技术层面上成长的一个项目,那便是开源的plain framework(简称PF),我会在这里分享一些关于程序设计的一些心得。

      起源

      2014年的7月左右,本着对于自己技术的不断提高,我正式将之前的plain server项目进行整理,准备写一个可以方便使用的框架。具体原因主要是发觉自己在工作中非常需要,以及技术层面上需要突破,而且也想革新下在此领域里参差不齐的格局。
      一个人的野心便成为了他的动力,到了如今我也还是这样想。可是在这方面却已经有了许多成熟的框架,诸如ACE、 ZeroMQ等,不过却感觉使用起来并不怎么方便,要么就是功能太多,要么就是太单一。作为一个网络应用,需要的几个模块我便做出了总结:文件、网络、数据库、缓存、脚本。这五大部分,是如今应用中非常流行的,相信只要提供好的支持后这样的框架可以解决大部分应用的问题。
      特别是做游戏的朋友,可以发现一些国内的好框架,比如云风的skynet。我是在2012年的时候接触到了,那个时候我刚刚进入这个行业,对于脚本lua也在他的文章里受益匪浅。在此我向这些开源和分享知识的前辈们,表示由衷的感谢。不过skynet是使用C和lua结合的框架,这大概是因为个人爱好的原因。不过每个语言都不能尽善尽美,C++的优点在于对象化,而C呢在于简洁,其实C++本来是在C的基础上发展的,是更高层次的语言,所以从本身来说有一定优势,同时便失去简洁。我对C/C++没有任何偏好,不过我对第一个让我入门的PHP感到非常大的兴趣,脚本语言的魅力就是对象化而又不失其简洁,同时我就讨厌如JAVA一般的环境配置。
      无论怎样也好,从语言上来说,每种语言都是方便开发者使用,而PF的初衷也是这样。
      于是我从2014年开始了长达三年左右的开源过程……

      结构

      从代码的结构上来说,五大模块已经全部支持,不过我对结构要求的比较细。可能出于强迫症的原因,在期间我对该结构进行了无数次的更改。
      basic -- 基础支持
      cache -- 缓存
      db -- 数据库
      engine -- 引擎(主控制)
      file -- 文件操作
      net -- 网络
      script -- 脚本
      sys -- 系统
      util -- 工具

      版本

      在1.0-1.1的版本中,使用了C99的少部分特性,不过是以C++98的标准来编写的,这样适合大多数的开发者使用。
      在2.0以后的版本,使用C++11。因为我注意到它可以简化编码,而且似乎也比较高效,当我完全替换了C++11的模式后,才发现C++17标准已经出来,实在是让人意想不到,相比的C还停留在C11阶段。从目前来看,C++11的项目不会过时,大家也没有必要担心。

      接口

      待续……

      编译

      现阶段PF只支持LINUX上的编译和运行,会在后期支持WINDOWS的版本,因为之前许多朋友们出现了编译上的一些错误,为了降低编译的难度将会分开一个单独的仓库。
      在2.0rc版本里,在框架目录下使用make就可以编译框架和生成一个简单的pf_simple测试应用了,这比之前的简单很多。
      不过需要准备的环境是:升级到支持编译C++11的gcc、了解ODBC的配置。

      测试

    /**
     * GLOBALS["default.engine.frame"] = number;      //default 100.
     * GLOBALS["default.net.open"] = bool;            //default false.
     * GLOBALS["default.net.service"] = bool;         //default false.
     * GLOBALS["default.net.service_ip"] = string;    //default "".
     * GLOBALS["default.net.service_port"] = number;  //default 0.
     * GLOBALS["default.net.conn_max"] = number;      //default NET_CONNECTION_MAX.
     * GLOBALS["default.script.open"] = bool;         //default false.
     * GLOBALS["default.script.rootpath"] = string;   //default SCRIPT_ROOT_PATH.
     * GLOBALS["default.script.workpath"] = string;   //default SCRIPT_WORK_PATH.
     * GLOBALS["default.script.bootstrap"] = string;  //default "bootstrap.lua".
     * GLOBALS["default.script.type"] = number;       //default pf_script::kTypeLua.
     * GLOBALS["default.cache.open"] = bool;          //default fasle.
     * GLOBALS["default.cache.service"] = bool;       //default fasle.
     * GLOBALS["default.cache.conf"] = string;        //default "".
     * GLOBALS["default.cache.key_map"] = number;     //default ID_INVALID.
     * GLOBALS["default.cache.recycle_map"] = number; //default ID_INVALID.
     * GLOBALS["default.cache.query_map"] = number;   //default ID_INVALID.
     * GLOBALS["default.db.open"] = bool;             //default fasle.
     * GLOBALS["default.db.type"] = number;           //default kDBConnectorTypeODBC.
     * GLOBALS["default.db.server"] = string;         //default "".
     * GLOBALS["default.db.user"] = string;           //default "".
     * GLOBALS["default.db.password"] = string;       //default "".
     **/
    
    #include "main.h"
    #include "net.h"
    #include "packet/sayhello.h"
    
    //The script reload function.
    void reload() {
      if (is_null(ENGINE_POINTER)) return;
      auto env = ENGINE_POINTER->get_script();
      if (is_null(env)) return;
      env->reload("preload.lua");
    }
    
    //The test engine main loop event 1.
    int32_t times = 0;
    void main_loop(pf_engine::Kernel &engine) {
      std::cout << "main_loop ..." << std::endl;
      ++times;
      if (times > 10)
        std::cout << "main_loop exited by 10 times" << std::endl;
      else
        engine.enqueue([&engine](){ main_loop(engine); });
    }
    
    //The test engine main loop event 2.
    void main_loop1(pf_engine::Kernel &engine) {
      std::cout << "main_loop1 ..." << std::endl;
      ++times;
      if (times > 20)
        std::cout << "main_loop1 exited by 20 times" << std::endl;
      else
        engine.enqueue([&engine](){ main_loop1(engine); });
    }
    
    //Net test.
    pf_net::connection::Basic *connector{nullptr};
    void main_nconnect(pf_engine::Kernel &engine,
                       pf_net::connection::manager::Connector &mconnector) {
      mconnector.tick();
      if (is_null(connector)) {
        connector = mconnector.connect(
            "127.0.0.1", GLOBALS["default.net.service_port"].uint16());
      } else {
        static uint32_t last_time = 0;
        auto tickcount = TIME_MANAGER_POINTER->get_tickcount();
        if (tickcount - last_time > 5000) {
          SayHello packet;
          packet.set_str("hello ...");
          connector->send(&packet);
          last_time = tickcount;
        }
      }
      engine.enqueue([&engine, &mconnector](){ main_nconnect(engine, mconnector); });
    }
    
    //DB test.
    void db_test(pf_engine::Kernel &engine) {
      auto db = engine.get_db();
      if (is_null(db)) return;
      if (db->isready()) {
        db_query_t db_query;
        pf_db::Query query(&db_query);
        if (!query.init(db)) return;
        query.set_tablename("t_test");
        query.select("*");
        query.from();
        query.limit(1);
        if (query.execute()) {
          pf_basic::io_cwarn("------------------------db---------------------------");
          db_fetch_array_t fectch_array;
          query.fetcharray(fectch_array);
          pf_basic::io_cdebug("db_test keys: ");
          for (pf_basic::type::variable_t &key : fectch_array.keys)
            std::cout << key.string() << std::endl;
          pf_basic::io_cdebug("db_test values: ");
          for (pf_basic::type::variable_t &val : fectch_array.values)
            std::cout << val.string() << std::endl;
          pf_basic::io_cwarn("------------------------db---------------------------");
        }
      } else {
        engine.enqueue([&engine](){ db_test(engine); });
      }
    }
    
    int32_t main(int32_t argc, char * argv[]) {
      /* Base config. */
      GLOBALS["app.debug"] = true;
      GLOBALS["app.name"] = "simple";
    
      //Net.
      GLOBALS["default.net.open"] = true;
      GLOBALS["default.net.service"] = true;
      GLOBALS["default.net.service_port"] = 12306;
    
      //DB.
      GLOBALS["default.db.open"] = true;
      GLOBALS["default.db.server"] = "pf_test";
      GLOBALS["default.db.user"] = "root";
      GLOBALS["default.db.password"] = "mysql";
    
      //Script.
      GLOBALS["default.script.open"] = true;
    
      /* engine. */
      pf_engine::Kernel engine;
      pf_engine::Application app(&engine);
    
      /* command handler. */
      app.register_commandhandler("--reload", "lua script reload.", reload);
    
      /* engine event. */
      engine.enqueue([](){ std::cout << "main loop function1" << std::endl; });
      engine.enqueue([&engine](){ main_loop(engine); });
      engine.enqueue([&engine](){ main_loop1(engine); });
      engine.enqueue([&engine](){ db_test(engine); });
    
      /* net init. */
      pf_net::connection::manager::Connector mconnector;
      init_net_packets();
      mconnector.init(1);
      engine.enqueue([&engine, &mconnector](){ main_nconnect(engine, mconnector); });
    
      /* run */
      app.run(argc, argv);
      return 0;
    }

      在测试里面加入了基本的网络、脚本、数据库的测试,大家可以先尝试摸索一下,从上述代码中可以见到现在框架的比1.0版本简洁许多。

      在1.0中我就用到了全局变量,在设计的时候感觉十分冗杂,所以2.0后统一使用GLOBALS来代替,这样使用起来也方便。

      最后

      匆匆的写了这么一些,希望大家不要见怪,等有时间会把这篇文章修改好的。

      地址

      https://github.com/viticm/plainframework

  • 相关阅读:
    React Hooks 全解(一)
    Google搜索技巧
    #!/usr/bin/python3 和 #!/usr/bin/env python3 的区别
    Python函数
    Python程序代码阅读
    画个爱心向你表白
    自学需要注意的点
    Python文件操作
    国内加速访问GitHub
    (九) -前端-异步编程
  • 原文地址:https://www.cnblogs.com/lianyue/p/6336852.html
Copyright © 2020-2023  润新知