Nodeos程序简介
Nodeos是eosio公链中最重要,最核心的程序,可以说搞定了nodeos,基本上就可以说掌握了eosio整个设计精髓。
除了nodeos程序,其他的模块都是工具或者支撑程序。
Nodeos程序可以说几乎完成了eosio公链所有的功能,核心功能总结如下:
- 最基本的合约运行(集成Wasm虚拟机,系统合约本地运行)
- 本地内存数据库(保存状态数据,链数据,配置数据等)
- 账户及权限系统(内置主合约实现,权限系统)
- 块打包与生产过程(块共识,块同步,DPOS算法等)
- 链RestApi封装(链访问API,合约状态访问API)
- 快照系统,实现节点快速保存,快速启动
Nodeos框架
Nodeos采用静态插件式的开发框架,核心代码结构请参考 EOSIO简介
在eosio中,所有的插件代码是通过appbase模块来组织管理与启动的,所有的插件都继承模版类plugin
template<typename Impl>
class plugin : public abstract_plugin {
public:
virtual void initialize(const variables_map& options) override {
//调用插件的plugin_initialize方法完成初始化
static_cast<Impl*>(this)->plugin_initialize(options);
}
virtual void startup() override {
//调用插件的plugin_startup方法启动插件
static_cast<Impl*>(this)->plugin_startup();
}
virtual void shutdown() override {
//调用插件的shutdown方法卸载插件
static_cast<Impl*>(this)->plugin_shutdown();
}
protected:
plugin(const string& name) : _name(name){}
private:
state _state = abstract_plugin::registered;
std::string _name;
};
class abstract_plugin {
public:
...
// 插件参数注册函数
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类的实现
class application {
private:
map<string, std::unique_ptr<abstract_plugin>> plugins; ///< all registered plugins
vector<abstract_plugin*> initialized_plugins; ///< stored in the order they were started running
vector<abstract_plugin*> running_plugins; ///< stored in the order they were started running
std::function<void()> sighup_callback;
map<std::type_index, erased_method_ptr> methods;
map<std::type_index, erased_channel_ptr> channels;
std::shared_ptr<boost::asio::io_service> io_serv;
execution_priority_queue pri_queue;
}
从上面的application的代码中,得到两个最重要的信息
- application全局管理了所有的注册的插件
- application提供了插件之间通信机制
Nodeos启动过程
所有C++程序的启动都是从main函数开始的,所以Nodeos也不例外,其启动过程依次如下
int main(int argc, char** argv) {
//调用app.initialize函数直接默认初始化链,网络,生产插件
if(!app().initialize<chain_plugin, net_plugin, producer_plugin>(argc, argv)) {
const auto& opts = app().get_options();
if( opts.count("help") || opts.count("version") || opts.count("full-version") || opts.count("print-default-config") ) {
return SUCCESS;
}
return INITIALIZE_FAIL;
}
//启动已经注册的插件
app().startup();
//程序开始运行
app().exec();
}
接下来依次展开app的initialize,startup函数如下
// 利用变参函数,动态初始化需要的插件
template<typename... Plugin>
bool initialize(int argc, char** argv) {
return initialize_impl(argc, argv, {find_plugin<Plugin>()...});
}
// 具体初始化在函数initialize_impl中完成
bool application::initialize_impl(int argc, char** argv, vector<abstract_plugin*> autostart_plugins) {
// 注册插件的启动参数
set_program_options();
// 初始化基本参数
...
// 调用initialize函数,逐个初始化每个插件
try {
for (auto plugin : autostart_plugins)
if (plugin != nullptr && plugin->get_state() == abstract_plugin::registered)
plugin->initialize(options);
bpo::notify(options);
} catch (...) {
std::cerr << "Failed to initialize\n";
return false;
}
}
void application::startup() {
// 按顺序逐个启动每个插件
try {
for( auto plugin : initialized_plugins ) {
if( is_quiting() ) break;
plugin->startup();
}
} catch( ... ) {
clean_up_signal_thread();
shutdown();
throw;
}
}
接下来逐个分析每个插件的plugin_initialize,plugin_startup方法就可以了解在程序启动时,都具体做了哪些事情
总结
- nodeos中chain,net,producer插件是默认启动的,其他的插件需要配置才能启动
- nodeos程序中最重要的插件是chain,producer两个插件,chain负责链启动,访问等,producer负责块生产
- 如果是单点完全可以不用启动net插件,但是区块链是多点共识,所以一般至少会启动三个节点,net插件是必须的
- 要通过API访问链,需要配置chain_api_plugin插件,net插件通过api控制需要配置net_api_plugin插件
- application除了管理插件,最重要的就是实现了高效快捷的插件事件机制,application值得我们学习