• 开源服务器设计总计(plain framework2020年总计)


    2020年注定会被历史铭记,世界遭受着一场前所未有的灾难,这种灾难到现在还在持续。还记得19年末的时候,那时候听到一点点消息,哪里想得到年关难过,灾难来的让人猝不及防。由于疫情防控,2020年感觉转瞬即逝,仿佛晃眼的功夫。本来做些自身职业上的改变,去年因为自身的原因因此搁浅。想想这几年自己维护的这个框架并未有太大的起色,甚者一度遗忘了它,所规划的很多内容并未得到实现。自己的懒惰搁浅,未免是对生命的严重浪费。今年的虽然暂时没有太多的计划,但是改变已经走在路上,希望这对自己的人生有所帮助。plain framework原本是面向游戏服务器设计的,不过在设计上尽量兼容所有的网络应用,从14年开始到现在已经六七年了,经过多次的修改,也扩充了不少的接口,在使用上更加的便捷。在这篇总结中,我主要讲述的是最近加入的控制台(console)模块,其实这个模块在许多框架中是很常见的。未来的技术日新月异,如果大家对编程上面有所兴趣,不妨可以做一定的参考,如有不足的地方也请指正,我将认真的思考其中遗留的问题。在这里我也祝愿大家在2021年,在风雨之后迎来希望的彩虹!

    总结

      在以前的文章中,我已经总结过plain framework的成长过程,从C++98到C++11经历了不小的变化。如今的目录结构和当初很不同,从参考到自己的一些设计,让整个框架越来越好用越来越高效是最终的目的。但是这种改变是漫长的,而且总觉得力不从心,许多的设计虽然有过临时的想法始终没能得到执行。当前这个框架现阶段如果直接用于项目中问题不大,却不能保证有所隐藏的一些BUG。今年我要做出一些改变,不单单做游戏方面的设计,我对于未来的技术也有过兴趣,如在之前的项目中使用过VUE框架来做后台的相应设计。不过相对于未来,算法始终是大方向以及技术上需要突破的,比如AI人工智能,其中的算法需要许多的数学知识,但这些相应的知识我自己要么遗忘要么还没认真接触,作为自己的兴趣后续会向这些方向研究。

      人生总要有些改变,支持迟来和早来!因此我需要有段时间来放空脑袋,想一想未来的方向了。随着年龄的增长就产生了一种莫名的危机感,在职业上也有些乏味缺少新鲜,但是生活总是要继续的。一个人如果有了计划而不执行,到了没有精力和时间去做得时候,那时候再后悔未免可笑。因此我觉得一个人就得坚定地向着自己的目标走去,就算目标看来那样遥不可及,但我们可以不断改变策略,毕竟没有人随随便便就成功,但如果不去行动那么连成功的机会都没有。

      希望大家都能慢慢接近自己的理想,也希望这场人类的灾难早点过去!

    控制台(console)

      控制台是在应用中提供调试的工具,一般情况下可以来分析应用运行过程中一些数据。在经典的操作系统中,控制台实在是太常见了,如windows中的命令行控制器,可以使用相应的命令对系统或者应用进行运行、调试和分析。

      

       plain framework加入控制台的目的,也是为了调试以及在应用运行中做一些调试和处理,提供了比较丰富的命令接口使得外部注册命令比较容易。在服务器的设计中,很多时候想要看看应用的线程内存情况,还有网络的链接和数据的收发情况。增加控制台,有助于我们在测试的时候,对不同情况下特别是压力测试时分析出重要的数据,这有助于帮助我们对程序进行优化。目前PF 中的命令不多,主要几个常见的命令,后续会继续完善。

      下图为PF中控制台的调试(包括在LINUX下的编译部分),目前仅支持网络调试(直接输入的方式很快集成,由于感觉用处不大暂时没实现):

    部分代码

      由于控制台需要使用网络命令行,因此在框架中增加了standard的网络协议(protocol),这个网络协议是遇到换行便将内容读出并调用注册的执行接口:

    #include "pf/basic/string.h"                                                       
    #include "pf/net/stream/input.h"                                                   
    #include "pf/net/stream/output.h"                                                  
    #include "pf/net/connection/basic.h"                                               
    #include "pf/net/connection/manager/listener.h"                                    
    #include "pf/net/protocol/standard.h"                                              
                                                                                       
    using namespace pf_basic::string;                                                  
    using namespace pf_net::protocol;                                                  
                                                                                       
    bool Standard::command(connection::Basic *connection, uint16_t count) {            
      if (connection->is_disconnect()) return false; //Leave this when not connected.
      stream::Input *istream = &connection->istream();                                 
      auto line = istream->readline();                                                 
      if (!line.empty()) {                                                             
        if (!connection->check_safe_encrypt()) return false;                           
        auto listener = connection->get_listener();                                    
        if (!is_null(listener)) {                                                      
          rtrim(line); // Remove '
    ' '
    ' or other words on last.                     
          auto callback = listener->get_standard_callback();                           
          if (callback) callback(line, connection);                                    
        }                                                                                                                                                  
      }                                                                                
      return true;                                                                     
    }                                                                                  
                                                                                       
    bool Standard::send(connection::Basic *connection, packet::Interface *packet) { 
      return true;                                                                     
    }                                                                                  

      在处理命令的时候注册的回调函数如下:

    void console_net_handle(                                                                                       
        const std::string &cmd, pf_net::connection::Basic *connection) {            
      using namespace pf_console;                                                                                  
      using namespace pf_basic::string;                                                                            
      if (is_null(ENGINE_POINTER)) return;                                                                         
      auto console = ENGINE_POINTER->get_console();                                                                
      if (is_null(console)) return;                                                                                
      if ("quit" == cmd) {                                                                                         
        connection->exit();                                                                                        
        return;                                                                                                    
      }                                                                                                            
      StringInput input(cmd);                                                                                      
      NetOutput output(connection);                                                                                
      console->run(&input, &output);                                                                               
    }                 

      整个控制台的实现目录结构如下:

      控制台应用代码:

    #include "pf/basic/string.h"
    #include "pf/console/argv_input.h"
    #include "pf/console/array_input.h"
    #include "pf/console/commands/app.h"
    #include "pf/console/commands/help.h"
    #include "pf/console/commands/list.h"
    #include "pf/basic/logger.h"
    #include "pf/console/application.h"
    
    using namespace pf_console;
    using namespace pf_interfaces::console;
    using namespace pf_basic::string;
    
    uint8_t Application::run(Input *input, Output *output) {
      std::unique_ptr<Input> input_temp;
      std::unique_ptr<Output> output_temp;
      if (is_null(input)) {
        unique_move(Input, new ArgvInput(), input_temp);
        input = input_temp.get();
      }
      if (is_null(output)) {
        unique_move(Output, new Output(), output_temp);
        output = output_temp.get();
      }
      configure_IO(input, output);
      uint8_t exit_code{0};
      try {
        exit_code = do_run(input, output);
      } catch (std::exception &e) {
        std::cout << "Application::run get error!!!: " << e.what() << std::endl;
      }
      return exit_code;
    }
    
    uint8_t Application::do_run(Input *input, Output *output) {
      if (input->has_parameter_option({"--version", "-V"})) {
        output->write_ln(get_long_version());
        return 0;
      }
      try {
        input->bind(get_definition());
      } catch(...) {
    
      }
      auto name = get_command_name(input);
      std::unique_ptr<Input> input_temp;
      if (input->has_parameter_option({"--help", "-h"}, true)) {
        if (name == "") {
          name = "help";
          unique_move(Input,
              new ArrayInput({{"command_name", default_command_name_}}),
              input_temp);
          input = input_temp.get();
        } else {
          want_helps_ = false;
        }
      }
      if (name == "") {
        name = default_command_name_;
        auto definition = get_definition();
        definition->set_argument(InputArgument("command",
            InputArgument::kModeOptional,
            definition->get_argument("command").get_description(), name));
      }
      Command *command{nullptr};
      try {
        running_command_ = nullptr;
        command = find(name);
      } catch (...) {
    
      }
      if (is_null(command)) {
        FAST_ERRORLOG(CONSOLE_MODULENAME, 
                      "[console] (Application::run)"
                      " can't find the command: %s", 
                      name.c_str());
        return 1;
      }
      running_command_ = command;
      auto exit_code = do_runcommand(command, input, output);
      return exit_code;
    }
    
    InputDefinition *Application::get_definition() {
      if (is_null(definition_)) {
        std::unique_ptr<InputDefinition> temp(new InputDefinition());
        *temp = get_default_input_definition();
        definition_ = std::move(temp);
        if (single_command_) {
          if (is_null(definition_temp_))    {
            unique_move(InputDefinition, new InputDefinition(), definition_temp_);
            *definition_temp_ = *definition_;
          }
          definition_temp_->set_arguments({});
          return definition_temp_.get();
        }
      }
      return definition_.get();
    }
    
    std::string Application::get_long_version() const {
      std::string r{"Console Tool"};
      if (name_ != "") {
        if (version_ != "") {
          r = name_ + " " + version_;
        }
        r = name_;
      }
      return r;
    }
    
    Command *Application::add(Command *command) {
      // std::cout << "Add command: " << command->name() << std::endl;
      if (!command->is_enabled()) {
        return nullptr;
      }
      init();
      command->set_application(this);
      // Will throw if the command is not correctly initialized.
      command->get_definition();
      command->configure();
      auto name = command->name();
      if (name == "") {
        throw std::logic_error("command cannot have an empty name.");
      }
      if (commands_.find(name) != commands_.end()) {
        return commands_[name].get();
      }
      std::unique_ptr<Command> temp;
      unique_move(Command, command, temp);
      commands_[name] = std::move(temp);
      for (auto const &alias : command->get_aliases()) {
        command_aliases_[alias] = name;
      }
      return command;
    }
    
    Command *Application::get(const std::string &_name) {
      auto name = get_command_real_name(_name);
      if ("" == name) {
        std::string e = "The command "" + _name + "" does not exist.";
        throw std::invalid_argument(e);
      }
      if (commands_.find(name) == commands_.end()) return nullptr;
      auto command = commands_[name].get();
      if (want_helps_) {
        want_helps_ = false;
        auto help_command = get("help");
        help_command->set_command(command);
        return help_command;
      }
      return command;
    }
    
    std::vector<std::string> Application::get_namespaces() {
      std::vector<std::string> r;
      auto commands = all();
      for (auto it = commands.begin(); it != commands.end(); ++it) {
        if (it->second->is_hidden()) continue;
        auto temp = extract_all_namespace(it->first);
        for (const auto &one : temp) r.emplace_back(one);
        for (const auto &alias : it->second->get_aliases()) {
          auto temp1 = extract_all_namespace(alias);
          for (const auto &one : temp1) r.emplace_back(one);
        }
      }
      // * The result maybe need use array_unique to remove the same values.
      return r;
    }
    
    std::string Application::find_namespace(const std::string &_namespace) {
      return "";
    }
    
    Command *Application::find(const std::string &name) {
      init();
      for (auto it = commands_.begin(); it != commands_.end(); ++it) {
        if (!is_null(it->second)) {
          for (const auto &alias : it->second->get_aliases()) {
            if ("" == command_aliases_[alias]) 
              command_aliases_[alias] = it->second->name();
          }
        } else {
          std::cout << "find no command: " << it->first << std::endl;
        }
      }
      return get(name);
    }
    
    std::map<std::string, Command *>
    Application::all(const std::string &_namespace) {
      std::map<std::string, Command *> r;
      init();
      if ("" == _namespace) {
        for (auto it = commands_.begin(); it != commands_.end(); ++it) {
          r[it->first] = it->second.get();
        }
        return r;
      }
      for (auto it = commands_.begin(); it != commands_.end(); ++it) {
        if (_namespace == extract_namespace(it->first)) {
          r[it->first] = it->second.get();
        }
      }
      return r;
    }
    
    Application &Application::set_default_command(
        const std::string &name, bool is_single_command) {
      default_command_name_ = name;
      if (is_single_command) {
        // Ensure the command exist
        find(name);
        single_command_ = true;
      }
      return *this;
    }
    
    uint8_t Application::do_runcommand(
        Command *command, Input *input, Output *output) {
      // std::cout << "do_runcommand: " << command->name() << std::endl;
      return command->run(input, output);
    }
    
    std::string Application::get_command_name(Input *input) const {
      return single_command_ ? default_command_name_ : input->get_first_argument();
    }
    
    InputDefinition Application::get_default_input_definition() const {
      std::vector<InputParameter *> p;
      std::unique_ptr<InputParameter> p1(new InputArgument(
            "command", InputParameter::kModeRequired,
            "The command to execute", ""
            ));
      p.emplace_back(p1.get());
      std::unique_ptr<InputParameter> p2(new InputOption(
            "--help", "-h", InputParameter::kModeNone,
            "Display help for the given command. When no command"
            " is given display help for the" + default_command_name_ + "command", ""
            ));
      p.emplace_back(p2.get());
      std::unique_ptr<InputParameter> p3(new InputOption(
            "--quiet", "-q", InputParameter::kModeNone,
            "Do not output any message", ""
            ));
      p.emplace_back(p3.get());
      std::unique_ptr<InputParameter> p4(new InputOption(
            "--verbose", "-v|vv|vvv", InputParameter::kModeNone,
            "Increase the verbosity of messages: 1 for normal output, "
            "2 for more verbose output and 3 for debug", ""
            ));
      p.emplace_back(p4.get());
      std::unique_ptr<InputParameter> p5(new InputOption(
            "--version", "-V", InputParameter::kModeNone,
            "Display this application version", ""
            ));
      p.emplace_back(p5.get());
      std::unique_ptr<InputParameter> p6(new InputOption(
            "--ansi", "", InputParameter::kModeNone,
            "Force ANSI output", ""
            ));
      p.emplace_back(p6.get());
      std::unique_ptr<InputParameter> p7(new InputOption(
            "--no-ansi", "", InputParameter::kModeNone,
            "Disable ANSI output", ""
            ));
      p.emplace_back(p7.get());
      std::unique_ptr<InputParameter> p8(new InputOption(
            "--no-interaction", "-n", InputParameter::kModeNone,
            "Do not ask any interactive question", ""
            ));
      p.emplace_back(p8.get());
    
      return InputDefinition(p);
    }
    
    std::vector<Command *> Application::get_default_commands() const {
      std::vector<Command *> r;
      return r;
    }
    
    std::string Application::get_abbreviation_suggestions(
        const std::vector<std::string> &abbrevs) const {
      return "";
    }
    
    std::vector<std::string> Application::find_alternatives(
        const std::string &name, const std::vector<std::string> &collection) const {
      return {};
    }
    
    void Application::configure_IO(Input *input, Output *output) {
      if (input->has_parameter_option({"--ansi"}, true)) {
        output->set_decorated(true);
      } else if (input->has_parameter_option({"'--no-ansi'"}, true)) {
        output->set_decorated(false);
      }
    }
    
    std::vector<std::string> Application::extract_all_namespace(
        const std::string &name) {
      std::vector<std::string> r;
      std::vector<std::string> parts;
      explode(name.c_str(), parts, ":", true, true);
      for (const auto &part : parts) {
        if (r.size() > 0) {
          std::string temp = r[r.size() - 1] + ":" + part;
          r.emplace_back(temp);
        } else {
          r.emplace_back(part);
        }
      }
      return r;
    }
    
    std::string Application::extract_namespace(
        const std::string &name, int32_t limit) const {
      return "";
    }
    
    void Application::init() {
      if (initialized_) return;
      initialized_ = true;
      // std::cout << "Application::init" << std::endl;
      add(new commands::Help());
      add(new commands::List());
      add(new commands::App());
    }

      命令实现代码:

    #include "pf/support/helpers.h"
    #include "pf/console/application.h"
    #include "pf/console/command.h"
    
    using namespace pf_support;
    using namespace pf_console;
    
    // Static member must be initialized.
    std::string Command::default_name_{"unknown"};
       
    void Command::merge_application_definition(bool merge_args) {
      if (is_null(app_) or !is_null(full_definition_)) return;
      unique_move(InputDefinition, new InputDefinition(), full_definition_);
      full_definition_->set_options(array_values(definition_->get_options()));
      full_definition_->add_options(
        array_values(app_->get_definition()->get_options()));
      if (merge_args) {
        full_definition_->set_arguments(
            array_values(app_->get_definition()->get_arguments()));
        full_definition_->add_arguments(array_values(definition_->get_arguments()));
      } else {
        full_definition_->set_arguments(array_values(definition_->get_arguments()));
      }
    }
    
    uint8_t Command::run(Input *input, Output *output) {
      uint8_t r{0};
      merge_application_definition();
      // bind the input against the command specific arguments/options
      try {
        input->bind(get_definition(), is_parse_input());
      } catch (std::exception &e) {
        if (!ignore_validation_errors_)
          throw std::runtime_error(e.what());
      }
      initialize(input, output);
    
      // Set process title.
      if (!process_title_.empty()) {
    
      }
      if (input->is_interactive()) {
        interact(input, output);
      }
    
      // The command name argument is often omitted when a command is executed 
      // directly with its run() method.
      // It would fail the validation if we didn't make sure the command argument 
      // is present, since it's required by the application.
      if (input->has_argument("command") && input->get_argument("command") == "") {
        input->set_argument("command", name_);
      }
    
      input->validate();
    
      if (code_) {
        r = code_(input, output);
      } else {
        r = execute(input, output);
      }
    
      return r;
    }

    更多

      可以在github上找到完整的项目:https://github.com/viticm/plain

    作者:viticm 出处: http://www.cnblogs.com/lianyue/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。
  • 相关阅读:
    Flink 电商实时数仓(二十三):ClickHouse基础(二)使用基础(2)ClickHouse 的安装(centos)
    Flink 电商实时数仓(二十二):ClickHouse基础(一)使用基础(1)ClickHouse 入门
    Flink 源码(二十六):Flink 内存管理(二)内存数据结构 、管理器
    Flink 源码(二十五):Flink 内存管理(一)内存模型与内存数据结构
    Flink 源码(二十四):Flink 任务调度机制(五)调度
    460. LFU Cache (solution 1)
    785. Is Graph Bipartite? (是否二分图)
    1318. Minimum Flips to Make a OR b Equal to c
    211. Add and Search Word
    188. Best Time to Buy and Sell Stock IV
  • 原文地址:https://www.cnblogs.com/lianyue/p/14643293.html
Copyright © 2020-2023  润新知