【问链-EOS公开课】第十三课 EOS插件机制深入解析

插件体系
EOS插件由三层类来实现。

最顶层是抽象类abstract_plugin,定义了插件的基本接口。
中间层是插件模板类plugin,主要用来解决插件之间依赖调用。
最底层是具体插件类,专注单个插件的业务功能实现。
【问链-EOS公开课】第十三课 EOS插件机制深入解析_第1张图片
nodeos进程启动后第一步是注册插件,在eos/programs/nodeos/main.cpp中看到

int main(int argc, char** argv)
{
      ...
      // 注册插件
      app().register_plugin();
     ...
      app().register_plugin();
      // app初始化
      if(!app().initialize(argc, argv))
         return -1;
      ...
}

应用程序通过app()返回一个application类的实例对象,这里采用单例模式,保证整个系统访问的是同一个全局对象,具体实现:

libraries/appbase/application.cpp

application& application::instance() {
    static application _app;
return _app;
}
application& app() { return application::instance(); }

在加载使用插件前,需要通过register_plugin()函数将插件注册到application的plugins插件集合中,plugins是一个map容器,通过键值对管理插件名称和插件对象指针,方便通过插件名称查找插件对象。

static appbase::abstract_plugin& _producer_plugin = app().register_plugin();
class application
{
    …
 template auto& register_plugin() {
  auto existing = find_plugin();// 根据类名字查找已经注册的插件集
if(existing)// 已经注册过的就不再重复注册
  return *existing;// 返回插件引用
auto plug = new Plugin();//还没注册的就new一个插件对象
plugins[plug->name()].reset(plug); // 根据类名注册到插件集中
plug->register_dependencies(); // 注册插件的下一级依赖
return *plug;//返回注册插件引用
 }
 …
 map plugins;
 …
}

查找插件
注册插件集合使用了application的map类成员plugins,注册key是插件类名,value是指向插件抽象对象的指针,并且使用了std::unique_ptr防止插件对象被非法引用。插件抽象类定义了插件的必要接口,包括当前状态、名字、初始化、启停接口,所有的具体插件都要实现这些接口。

map> plugins; ///< 所有注册的插件对象

// 插件抽象类定义了插件的必要接口
   class abstract_plugin {
      public:
         enum state {
            registered, ///< 插件已经构建但还没做任何事情 the plugin is constructed but doesn't do anything
            initialized, ///< 插件已经初始化所有状态,但仍处于待启动状态 the plugin has initialized any state required but is idle
            started, ///< 插件已经启动,在运行中  the plugin is actively running
            stopped ///< 插件已经停止 the plugin is no longer running
         };

         virtual ~abstract_plugin(){}
         virtual state get_state()const = 0;  // 插件当前状态
         virtual const std::string& name()const  = 0; // 名字
         virtual void set_program_options( options_description& cli, options_description& cfg ) = 0; 
              // 设定命令行/配置文件中允许的可配置选项
         virtual void initialize(const variables_map& options) = 0; // 初始化
         virtual void startup() = 0; // 启动插件
         virtual void shutdown() = 0; // 停止插件
   };

// application的find_plugin模板成本函数
class application {
...
         template
         Plugin* find_plugin()const {
           // 利用boost工具获取插件类名,再到注册类集合中查找
            string name = boost::core::demangle(typeid(Plugin).name());
            return dynamic_cast(find_plugin(name));
         }
...
}

插件依赖注册
插件之间可能存在依赖关系,譬如net_api_plugin依赖net_plugin和http_plugin,即application想要使用net_api_plugin必须要保证另外两个插件也被注册。
具体插件通过实例化插件模板类来定义,需要指定具体插件类作为模板参数。在模板类的register_dependencies函数里调用了子类的plugin_requires函数,传入了一个空的函数闭包。

// 插件模板类,需要指定具体插件类作为模板参数
   template
   class plugin : public abstract_plugin {
       ...
         virtual void register_dependencies() {
            static_cast(this)->plugin_requires([&](auto& plug){});
         }
      ...
   }

我们看到具体插件类中,是通过宏APPBASE_PLUGIN_REQUIRES来定义plugin_requires,这个宏的参数指定了当前插件所依赖的其他插件。

#define APPBASE_PLUGIN_REQUIRES_VISIT( r, visitor, elem ) \
  visitor( appbase::app().register_plugin() );

#define APPBASE_PLUGIN_REQUIRES( PLUGINS )                               \
   template                                           \
   void plugin_requires( Lambda&& l ) {                                \
      BOOST_PP_SEQ_FOR_EACH( APPBASE_PLUGIN_REQUIRES_VISIT, l, PLUGINS ) \
   }

class net_api_plugin : public plugin {
public:
    // net_api_plugin依赖了net_plugin和http_plugin两个插件
   APPBASE_PLUGIN_REQUIRES((net_plugin) (http_plugin))
...
}

对宏展开如下,包含了对net_plugin和http_plugin的注册。

class net_api_plugin : public plugin {
public:
    void plugin_requires( Lambda&& l ) {
        lambda(appbase::app().register_plugin());
        lambda(appbase::app().register_plugin());
    }
...
}

lambda表达式的传入参数是注册后的插件对象引用,不过,register_dependencies里的lambda是[&](auto& plug){},实际执行体为空,所以没有对依赖的插件做进一步处理。

插件初始化、启停
插件模板类除了定义register_dependencies注册依赖,还定义了插件初始化、启动、停止三个方法。

initialize和startup方法同register_dependencies一样,调用具体子类的plugin_requires,但是传入了包含实际处理的lambda闭包,来调用所依赖的插件执行初始化/启动。下级插件完成处理后,执行本插件的具体插件类的处理方法plugin_initialize和plugin_startup。
启动程序

加载插件后,遍历调用initialized_plugins集合中各个插件实例的startup()函数,启动插件任务,例如producer_plugin插件的启动函数为producer_plugin::plugin_startup(),主要功能是循环生产区块:

void application::startup() {
       for (auto plugin : initialized_plugins)
           plugin->startup();
}
  
class plugin : public abstract_plugin {
       virtual void startup() override {
    …
        static_cast(this)->plugin_startup();
    …
    }
}
  
class producer_plugin : public appbase::plugin {
    …
       virtual void plugin_startup();
    …
}
  
void producer_plugin::plugin_startup()
{
…
    my->schedule_production_loop(); // 循环生产区块
…
}

shutdown方法由app统一调度所有已注册过(直接或间接注册)的插件shutdown,所以无需进一步调用依赖的插件执行。

  template
   class plugin : public abstract_plugin {
       ...
         virtual void initialize(const variables_map& options) override {
            if(_state == registered) {
               _state = initialized;
               // 对下级依赖插件调用初始化
               static_cast(this)->plugin_requires([&](auto& plug){ plug.initialize(options); });
               // 当前插件初始化
               static_cast(this)->plugin_initialize(options);
               //ilog( "initializing plugin ${name}", ("name",name()) );
               app().plugin_initialized(*this);  // 在application中记录
            }
            assert(_state == initialized); /// if initial state was not registered, final state cannot be initiaized
         }

         virtual void startup() override {
            if(_state == initialized) {
               _state = started;
               static_cast(this)->plugin_requires([&](auto& plug){ plug.startup(); });
               static_cast(this)->plugin_startup();
               app().plugin_started(*this);
            }
            assert(_state == started); // if initial state was not initialized, final state cannot be started
         }

         virtual void shutdown() override {
            if(_state == started) {
               _state = stopped;
               //ilog( "shutting down plugin ${name}", ("name",name()) );
               static_cast(this)->plugin_shutdown();
            }
         }
   ...
}

各个插件初始化并启动完成后,最后设置应用程序的信号处理函数,用来响应用户终止动作,例如,ctrl + c:

void application::exec() {
    sigint_set->async_wait
    io_serv->run(); // 异步等待信号事件发生。
    shutdown() // 应用退出后关闭插件。
}

总结
EOS采用石墨烯引擎为基础构建区块链,并且实现了一套灵活的模块化插件机制,在抽象插件类和具体功能类之间引入一层模板类,来将插件间依赖调用从具体类中解耦出来,有利于插件功能内聚以及新插件扩展。

你可能感兴趣的:(区块链那些事儿,区块链之风云往事)