EOS开源代码学习笔记(二)插件的初始化
先说明一个上篇笔记中的问题,之前我写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开发学习交流