【问链-EOS公开课】第十三课 EOS插件机制深入解析
插件體系
EOS插件由三層類來實現。
最頂層是抽象類abstract_plugin,定義了插件的基本接口。
中間層是插件模板類plugin,主要用來解決插件之間依賴調用。
最底層是具體插件類,專注單個插件的業務功能實現。
nodeos進程啟動后第一步是注冊插件,在eos/programs/nodeos/main.cpp中看到
應用程序通過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防止插件對象被非法引用。插件抽象類定義了插件的必要接口,包括當前狀態、名字、初始化、啟停接口,所有的具體插件都要實現這些接口。
插件依賴注冊
插件之間可能存在依賴關系,譬如net_api_plugin依賴net_plugin和http_plugin,即application想要使用net_api_plugin必須要保證另外兩個插件也被注冊。
具體插件通過實例化插件模板類來定義,需要指定具體插件類作為模板參數。在模板類的register_dependencies函數里調用了子類的plugin_requires函數,傳入了一個空的函數閉包。
我們看到具體插件類中,是通過宏APPBASE_PLUGIN_REQUIRES來定義plugin_requires,這個宏的參數指定了當前插件所依賴的其他插件。
#define APPBASE_PLUGIN_REQUIRES_VISIT( r, visitor, elem ) \visitor( appbase::app().register_plugin<elem>() );#define APPBASE_PLUGIN_REQUIRES( PLUGINS ) \template<typename Lambda> \void plugin_requires( Lambda&& l ) { \BOOST_PP_SEQ_FOR_EACH( APPBASE_PLUGIN_REQUIRES_VISIT, l, PLUGINS ) \}class net_api_plugin : public plugin<net_api_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<net_api_plugin> { public:void plugin_requires( Lambda&& l ) {lambda(appbase::app().register_plugin<net_plugin>());lambda(appbase::app().register_plugin<http_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<typename Impl>class plugin : public abstract_plugin {...virtual void initialize(const variables_map& options) override {if(_state == registered) {_state = initialized;// 對下級依賴插件調用初始化static_cast<Impl*>(this)->plugin_requires([&](auto& plug){ plug.initialize(options); });// 當前插件初始化static_cast<Impl*>(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<Impl*>(this)->plugin_requires([&](auto& plug){ plug.startup(); });static_cast<Impl*>(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<Impl*>(this)->plugin_shutdown();}}... }各個插件初始化并啟動完成后,最后設置應用程序的信號處理函數,用來響應用戶終止動作,例如,ctrl + c:
void application::exec() {sigint_set->async_waitio_serv->run(); // 異步等待信號事件發生。shutdown() // 應用退出后關閉插件。 }總結
EOS采用石墨烯引擎為基礎構建區塊鏈,并且實現了一套靈活的模塊化插件機制,在抽象插件類和具體功能類之間引入一層模板類,來將插件間依賴調用從具體類中解耦出來,有利于插件功能內聚以及新插件擴展。
總結
以上是生活随笔為你收集整理的【问链-EOS公开课】第十三课 EOS插件机制深入解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【问链-EOS公开课】第十二课 EOS整
- 下一篇: 【问链-EOS公开课】第十四课 EOS从