上一篇已經(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)目上族檬。下圖是石墨烯的源碼組織方式。
- 應(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
/librarires
和contracts
四部分組成踏枣,可以看出石墨烯的架構(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)昌阿。
插件注冊(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è)方法。
initialize
和startup
方法同register_dependencies
一樣偷卧,調(diào)用具體子類的plugin_requires
豺瘤,但是傳入了包含實(shí)際處理的lambda閉包,來調(diào)用所依賴的插件執(zhí)行初始化/啟動(dòng)听诸。下級(jí)插件完成處理后坐求,執(zhí)行本插件的具體插件類的處理方法plugin_initialize
和plugin_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ò)展掌逛。