EOS开源代码学习笔记(二)插件的初始化

in #cn7 years ago

先说明一个上篇笔记中的问题,之前我写eosiod的主体程序中,没有提及到插件的注册。因为我在源代码中没有找到调用插件注册的地方,今天看代码发现,如果插件不注册的话,是无法初始化application类里面的plugins容器的,如果有了解这里的大神,可以帮我解惑。

上篇笔记中,eosiod程序涉及到了三个插件,这三个插件也就是eosiod程序的核心所在。这三个插件分别为chain_plugin、net_plugin和http_plugin。这篇笔记写这三个插件的初始化过程。

一、插件的注册机制去哪里了

Application类的initialize方法是一个模板函数,内部调用initialize_impl函数进行初始化。通过find_plugin函数实现一个vector向量的初始化,如下所示:

template<typename... Plugin>
bool                 initialize(int argc, char** argv) {
   return initialize_impl(argc, argv, {find_plugin<Plugin>()...});
}
template<typename Plugin>
Plugin* find_plugin()const {
   string name = boost::core::demangle(typeid(Plugin).name());
   return dynamic_cast<Plugin*>(find_plugin(name));
}

函数返回abstract_plugin对象指针,find_plugin函数进行重载

abstract_plugin* application::find_plugin(const string& name)const
{
   auto itr = plugins.find(name); //plugins容器没有初始化
   if(itr == plugins.end()) {
      return nullptr;
   }
   return itr->second.get();
}

find_plugin函数功能是通过遍历plugins容器,返回插件对象的指针。需要注意的是,在application类里面对于函数find_plugin进行了重载,而plugins容器是需要插件进行注册来进行初始化的,但是没有发现插件注册的代码,也可能是我对于代码的理解不够。

这三个插件类继承自plugin基类,使用虚函数来进行多态实现,然后将三个功能模块作为插件添加到eosiod程序中,正是由于这种插件类型设计方式,eosiod的出块功能、p2p网络功能、http功能可能会部署在不同的机器上,给见证人节点以集群的方式部署自己的架构,提供了可能。

template<typename Plugin>
auto& register_plugin() {
   auto existing = find_plugin<Plugin>();
   if(existing)
      return *existing;
   auto plug = new Plugin();
   plugins[plug->name()].reset(plug);
   plug->register_dependencies();
   return *plug;
}

通过register_plugin()方法注册不同类型的插件,目的是初始化application类中的成员变量的plugins容器,只有注册过的插件,才能在find_plugin()方法里面初始化插件vector向量,才能进行后续的初始化工作。

二、插件的初始化过程

application 类中的initialize_impl方法的实现

bool application::initialize_impl(int argc, char** argv, vector<abstract_plugin*> autostart_plugins) {
   set_program_options();
   bpo::variables_map options;
   bpo::store(bpo::parse_command_line(argc, argv, my->_app_options), options);
   if( options.count( "help" ) ) {
      cout << my->_app_options << std::endl;
      return false;
   }
   // .....省略部分代码
   // 第二部分功能
   for (auto plugin : autostart_plugins)
      if (plugin != nullptr && plugin->get_state() == abstract_plugin::registered)
         plugin->initialize(options);
   bpo::notify(options);
   return true;
}

通过代码,我们知道,eosiod采用了boost库中的program_options库来解析命令行参数和配置。eosiod程序的初始化可以分成两部分功能,一部分是解析命令行参数和配置。二部分是根据配置信息对于每一个加载的插件进行初始化。

简要说明:使用program_options库来解析命令行参数,主要用到选项描述器(options_description)、选项存储器(variables_map,继承自map类)。使用options.count来检查是否输入了该参数。

第二部分是遍历注册的插件vector向量,获取每个插件对象指针,调用其插件子类的initialize函数进行初始化,参数为选项存储器options,即一个map类型的键值对链表,http_plugin插件类的定义。

class http_plugin : public appbase::plugin<http_plugin>
{
   public:
     http_plugin();
     virtual ~http_plugin();
     APPBASE_PLUGIN_REQUIRES()
     virtual void set_program_options(options_description&, options_description& cfg) override;
     void plugin_initialize(const variables_map& options);
     void plugin_startup();
     void plugin_shutdown();
     void add_handler(const string& url, const url_handler&);
     void add_api(const api_description& api) {
        for (const auto& call : api) 
           add_handler(call.first, call.second);
     }
   private:
     std::unique_ptr<class http_plugin_impl> my;
};

以前的文章:
EOS开源代码学习笔记(一) eosiod程序
qq群:694228458 ,用于eos开发学习交流
1521355302644.jpg