EOS源碼學(xué)習(xí):石墨烯引擎 & EOS插件機(jī)制

上一篇已經(jīng)分析了EOS節(jié)點(diǎn)程序eosd通過插件化的架構(gòu)組織各種服務(wù)功能赖条,本篇將介紹EOS所使用的石墨烯區(qū)塊鏈引擎代赁,并且介紹使用石墨烯引擎的eosd的插件管理和注冊(cè)機(jī)制。

石墨烯引擎

什么是石墨烯,根據(jù)官網(wǎng)介紹旦事,

The Graphene blockchain is not a monolithic application. It is composed of a variety of libraries and executables to provide deployable nodes.

石墨烯由一組庫和可執(zhí)行程序組成,用于提供可部署的區(qū)塊鏈節(jié)點(diǎn)的解決方案络它。石墨烯架構(gòu)已經(jīng)成功應(yīng)用于BitShare, Steem等區(qū)塊鏈項(xiàng)目上族檬。下圖是石墨烯的源碼組織方式。

石墨烯組件依賴關(guān)系圖
  • 應(yīng)用層Executables:最下層的可執(zhí)行程序有見證節(jié)點(diǎn)witness_node化戳,獨(dú)立錢包cli_wallet和構(gòu)造創(chuàng)世區(qū)塊的工具genesis_util单料。應(yīng)用層是對(duì)插件庫、核心API庫以及通用工具庫的調(diào)用組合点楼,實(shí)現(xiàn)其業(yè)務(wù)功能扫尖。
  • 插件層Plugin-Ins:插件對(duì)核心API進(jìn)行封裝以提供較為完整獨(dú)立的服務(wù),譬如區(qū)塊鏈查詢掠廓,交易驗(yàn)證執(zhí)行换怖,打包區(qū)塊,P2P網(wǎng)絡(luò)通信等服務(wù)蟀瞧。
  • 核心API層API/Core: 實(shí)現(xiàn)了基礎(chǔ)核心業(yè)務(wù)功能組件沉颂,譬如網(wǎng)絡(luò)、數(shù)據(jù)庫悦污,錢包相關(guān)功能(簽名铸屉,私鑰生成,驗(yàn)證)切端,區(qū)塊打包計(jì)算彻坛。
  • 通用工具庫FC utilities:提供業(yè)務(wù)無關(guān)的基礎(chǔ)功能工具。

本系列開篇簡(jiǎn)單介紹過EOS由programs/plugins/librarirescontracts四部分組成踏枣,可以看出石墨烯的架構(gòu)和EOS的架構(gòu)是很相近的昌屉,EOS增加了對(duì)智能合約的支持。實(shí)際上EOS并沒有直接用石墨烯的源代碼茵瀑,而是重寫了90%的代碼间驮,不過基本架構(gòu)是一樣的。

EOS插件機(jī)制

原始的石墨烯源碼就不必看了马昨,直接從eos入手了解石墨烯框架蜻牢。

插件體系

EOS插件由三層類來實(shí)現(xiàn)烤咧。

  • 最頂層是抽象類abstract_plugin,定義了插件的基本接口抢呆。
  • 中間層是插件模板類plugin煮嫌,主要用來解決插件之間依賴調(diào)用。
  • 最底層是具體插件類抱虐,專注單個(gè)插件的業(yè)務(wù)功能實(shí)現(xiàn)昌阿。
EOS插件體系

插件注冊(cè)

本系列上篇介紹,eosd進(jìn)程啟動(dòng)后第一步是注冊(cè)插件恳邀。

int main(int argc, char** argv)
{
      ...
      // 注冊(cè)插件
      app().register_plugin<net_api_plugin>();
     ...
      app().register_plugin<faucet_testnet_plugin>();
      // app初始化
      if(!app().initialize<chain_plugin, http_plugin, net_plugin>(argc, argv))
         return -1;
      ...
}

app() 返回application類靜態(tài)單例對(duì)象懦冰,調(diào)用類的模板成員函數(shù)register_plugin。

class application 
{
      ...
         template<typename Plugin>
         auto& register_plugin() {
            auto existing = find_plugin<Plugin>();   // 根據(jù)類名字查找已經(jīng)注冊(cè)的插件集
            if(existing)  // 已經(jīng)注冊(cè)過的就不再重復(fù)注冊(cè)
               return *existing; // 返回插件引用

            auto plug = new Plugin(); // 還沒注冊(cè)的就new一個(gè)插件對(duì)象
            plugins[plug->name()].reset(plug);  // 根據(jù)類名注冊(cè)到插件集中
            plug->register_dependencies(); // 注冊(cè)插件的下一級(jí)依賴
            return *plug; // 返回注冊(cè)插件引用
         }
   ...
}

查找插件

注冊(cè)插件集合使用了application的map類成員plugins谣沸,注冊(cè)key是插件類名刷钢,value是指向插件抽象對(duì)象的指針,并且使用了std::unique_ptr防止插件對(duì)象被非法引用乳附。插件抽象類定義了插件的必要接口内地,包括當(dāng)前狀態(tài)、名字赋除、初始化阱缓、啟停接口,所有的具體插件都要實(shí)現(xiàn)這些接口举农。

map<string, std::unique_ptr<abstract_plugin>> plugins; ///< 所有注冊(cè)的插件對(duì)象

// 插件抽象類定義了插件的必要接口
   class abstract_plugin {
      public:
         enum state {
            registered, ///< 插件已經(jīng)構(gòu)建但還沒做任何事情 the plugin is constructed but doesn't do anything
            initialized, ///< 插件已經(jīng)初始化所有狀態(tài)荆针,但仍處于待啟動(dòng)狀態(tài) the plugin has initialized any state required but is idle
            started, ///< 插件已經(jīng)啟動(dòng),在運(yùn)行中  the plugin is actively running
            stopped ///< 插件已經(jīng)停止 the plugin is no longer running
         };

         virtual ~abstract_plugin(){}
         virtual state get_state()const = 0;  // 插件當(dāng)前狀態(tài)
         virtual const std::string& name()const  = 0; // 名字
         virtual void set_program_options( options_description& cli, options_description& cfg ) = 0; 
              // 設(shè)定命令行/配置文件中允許的可配置選項(xiàng)
         virtual void initialize(const variables_map& options) = 0; // 初始化
         virtual void startup() = 0; // 啟動(dòng)插件
         virtual void shutdown() = 0; // 停止插件
   };

// application的find_plugin模板成本函數(shù)
class application {
...
         template<typename Plugin>
         Plugin* find_plugin()const {
           // 利用boost工具獲取插件類名颁糟,再到注冊(cè)類集合中查找
            string name = boost::core::demangle(typeid(Plugin).name());
            return dynamic_cast<Plugin*>(find_plugin(name));
         }
...
}

插件依賴注冊(cè)

插件之間可能存在依賴關(guān)系航背,譬如net_api_plugin依賴net_plugin和http_plugin,即application想要使用net_api_plugin必須要保證另外兩個(gè)插件也被注冊(cè)棱貌。

具體插件通過實(shí)例化插件模板類來定義沃粗,需要指定具體插件類作為模板參數(shù)。在模板類的register_dependencies函數(shù)里調(diào)用了子類的plugin_requires函數(shù)键畴,傳入了一個(gè)空的函數(shù)閉包。

// 插件模板類突雪,需要指定具體插件類作為模板參數(shù)
   template<typename Impl>
   class plugin : public abstract_plugin {
       ...
         virtual void register_dependencies() {
            static_cast<Impl*>(this)->plugin_requires([&](auto& plug){});
         }
      ...
   }

我們看到具體插件類中起惕,是通過宏APPBASE_PLUGIN_REQUIRES來定義plugin_requires,這個(gè)宏的參數(shù)指定了當(dāng)前插件所依賴的其他插件咏删。

#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兩個(gè)插件
   APPBASE_PLUGIN_REQUIRES((net_plugin) (http_plugin))
...
}

對(duì)宏展開如下惹想,包含了對(duì)net_plugin和http_plugin的注冊(cè)。

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表達(dá)式的傳入?yún)?shù)是注冊(cè)后的插件對(duì)象引用督函,不過嘀粱,register_dependencies里的lambda是[&](auto& plug){}激挪,實(shí)際執(zhí)行體為空,所以沒有對(duì)依賴的插件做進(jìn)一步處理锋叨。

插件初始化垄分、啟停

插件模板類除了定義register_dependencies注冊(cè)依賴,還定義了插件初始化娃磺、啟動(dòng)薄湿、停止三個(gè)方法。

initializestartup方法同register_dependencies一樣偷卧,調(diào)用具體子類的plugin_requires豺瘤,但是傳入了包含實(shí)際處理的lambda閉包,來調(diào)用所依賴的插件執(zhí)行初始化/啟動(dòng)听诸。下級(jí)插件完成處理后坐求,執(zhí)行本插件的具體插件類的處理方法plugin_initializeplugin_startup

shutdown方法由app統(tǒng)一調(diào)度所有已注冊(cè)過(直接或間接注冊(cè))的插件shutdown晌梨,所以無需進(jìn)一步調(diào)用依賴的插件執(zhí)行桥嗤。

   template<typename Impl>
   class plugin : public abstract_plugin {
       ...
         virtual void initialize(const variables_map& options) override {
            if(_state == registered) {
               _state = initialized;
               // 對(duì)下級(jí)依賴插件調(diào)用初始化
               static_cast<Impl*>(this)->plugin_requires([&](auto& plug){ plug.initialize(options); });
               // 當(dāng)前插件初始化
               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();
            }
         }
   ...
}

總結(jié)

EOS采用石墨烯引擎為基礎(chǔ)構(gòu)建區(qū)塊鏈,并且實(shí)現(xiàn)了一套靈活的模塊化插件機(jī)制派任,在抽象插件類和具體功能類之間引入一層模板類砸逊,來將插件間依賴調(diào)用從具體類中解耦出來,有利于插件功能內(nèi)聚以及新插件擴(kuò)展掌逛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末师逸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子豆混,更是在濱河造成了極大的恐慌篓像,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皿伺,死亡現(xiàn)場(chǎng)離奇詭異员辩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鸵鸥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門奠滑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妒穴,你說我怎么就攤上這事宋税。” “怎么了讼油?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵杰赛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我矮台,道長(zhǎng)乏屯,這世上最難降的妖魔是什么根时? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮辰晕,結(jié)果婚禮上蛤迎,老公的妹妹穿的比我還像新娘。我一直安慰自己伞芹,他們只是感情好忘苛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唱较,像睡著了一般扎唾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上南缓,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天胸遇,我揣著相機(jī)與錄音,去河邊找鬼汉形。 笑死纸镊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的概疆。 我是一名探鬼主播逗威,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼岔冀!你這毒婦竟也來了凯旭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤使套,失蹤者是張志新(化名)和其女友劉穎罐呼,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侦高,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嫉柴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奉呛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片计螺。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瞧壮,靈堂內(nèi)的尸體忽然破棺而出登馒,到底是詐尸還是另有隱情,我是刑警寧澤馁痴,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站肺孤,受9級(jí)特大地震影響罗晕,放射性物質(zhì)發(fā)生泄漏济欢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一小渊、第九天 我趴在偏房一處隱蔽的房頂上張望法褥。 院中可真熱鬧,春花似錦酬屉、人聲如沸半等。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杀饵。三九已至,卻和暖如春谬擦,著一層夾襖步出監(jiān)牢的瞬間切距,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工惨远, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谜悟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓北秽,卻偏偏與公主長(zhǎng)得像葡幸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贺氓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理蔚叨,服務(wù)發(fā)現(xiàn),斷路器掠归,智...
    卡卡羅2017閱讀 134,637評(píng)論 18 139
  • 年齡大了缅叠,越來越不喜歡和人打交道,轉(zhuǎn)而越來越喜歡獨(dú)處虏冻,去看看書肤粱,去旅游,想去探尋自己真正想要什么樣的生活才能活的更...
    新之桐閱讀 172評(píng)論 0 0
  • Spring頂級(jí)框架有眾多,那么接下的篇幅蛮穿,我將重點(diǎn)講解SpringCloud微框架的實(shí)現(xiàn) Spring 頂級(jí)項(xiàng)目...
    千萬之路剛開始閱讀 1,057評(píng)論 0 1
  • 失去庶骄?聽了兩遍八孝,讓我有點(diǎn)感覺的應(yīng)該是第一次找工作時(shí)的失敗汁咏。 大學(xué)畢業(yè)的我還特別的嫩列牺,家人說回家找吧嫡霞,離家比較近比較...
    夢(mèng)鐵凝閱讀 255評(píng)論 0 0
  • 書是讀不盡的遥赚,就讀盡也是無用,許多書都沒有一讀的價(jià)值拼岳。多讀一本沒有價(jià)值的書挎袜,便喪失可讀一本有價(jià)值的書的時(shí)間和精力,...
    做自己的CEO閱讀 608評(píng)論 2 10