元界(Metaverse)旨在搭建一个点对点的、由数字资产驱动生活和金融应用的虚拟世界,希望个人以及企业用户可以像现在的互联网一样,方便快捷地使用元界的各项区块链功能,特别是进行点对点的价值的交换过程。元界主链代码 Metaverse 是基于比特币技术体系中,选取以 libbitcoin 为框架,新增了Account、Miner、mongoose 等模块,大幅修改了 consensus 模块,新增了币奖励的 coinbase 交易。
接下来我将分一下 Metaverse 节点程序 mvsd 的启动流程,mvsd 的启动大致可以分为两块:start 与 run。
mvsd 是元界区块链节点运行程序,我们从其 main()
入手分析其启动流程。在 src/mvsd/main.cpp 文件的 main()
函数中,主要是读取配置文件,然后生成 executor 对象,调用其 menu() 方法:
executor host(metadata, cin, *out, *err); return host.menu() ? console_result::okay : console_result::failure;
executor
一如其名,是整个节点运转的执行者。其入口是 menu()
方法,在其中解析命令行参数,执行相应的操作。在这里我们只分析节点正常运行的分支,也就是初始化链以及运行:
auto result = do_initchain(); // false means no need to initial chain ... // There are no command line arguments, just run the server. return run();
在 do_initchain()
中主要干了三件事:
主逻辑全都在 run()
中:根据配置创建 server_node
,再将 handle_started
当作回调参数调用其 start()
方法初始化节点链接以加入区块链网络。
server_node
继承自 p2p_node
, p2p_node
又继承自 p2p
。因此对 server_node.start()
调用以及就开启了一条转调基类方法的传递链:
在 p2p_node.start()
中调用 block_chain_impl.start()
启动区块链
在 p2p.start()
中初始化线程池,启动订阅器,并将 handle_manual_started()
当作回调调用 manual_session.start()
启动 manual_session()
。
在回调 p2p.handle_manual_started()
函数中,启动线程池,装载 host 节点,并将 handle_started()
当作回调调用 seed_session.start()
启动 seed_session
。
在回调 p2p.handle_started()
函数中,转调 executor.handle_started()
,这样调用流程又回到了 executor
中。
至此 start 流程走完,start 主要是链接节点加入区块链网络。
在回调 executor.handle_started()
中调用 server_node.run()
进入各种服务的处理循环,直到节点退出运行。
在 server_node.run()
中:
首先启动 HttpServer
(用于处理 RESTful API
请求) 和 WebSocketServer
(用于处理 WebSocket
请求),然后将 handle_running
当作回调参数转调父类 p2p_node.run()
。
在 p2p_node.run()
中,将 handle_headers_synchronized()
当作回调参数调用 header_sync_session.start()
开始同步区块头数据。区块头数据同步完成后回调 handle_headers_synchronized()
回到 p2p_node
中。接着将 handle_running()
当作回调参数调用 block_sync_session.start()
开始同步区块数据。区块数据同步完成后回调 handle_running()
回到 p2p_node
中。在 handle_running
中设置区块高度,并调用父类 p2p.run()
。
在 p2p.run()
中,将 handle_running()
当作回调参数依次启动 inbound_session
、 outbound_session
用于处理节点的链入与链出。处理完毕之后回调到 p2p.handle_running()
,在这个方法中仅仅是转调回掉函数 server_node.handle_running()
,这样流程又回到了 server_node
中。
在 server_node.handle_running() 中启动各种服务:认证、查询、心跳、区块、交易服务。
bool server_node::start_services() { return start_authenticator() && start_query_services() && start_heartbeat_services() && start_block_services() && start_transaction_services(); }
然后回调 executor.handle_running()
,绕了一大圈终于又回到了起点 executor
。
在 executor.handle_running()
什么也没有干,仅仅是 log 打印了一下,因为各种服务都已经启动完毕,没什么事情需要 executor
操劳了。
打开链接 启动流程时序图 ,点击图片区域选择在新窗口打开,即可看到完整的启动流程时序图。
由于metaverse代码中大量使用嵌套回调handler,导致调用流程不容易理解,针对上面的 run()
流程,我写了一个简化版调用流程代码metaverse_run.cpp,这样就容易理解了。
// g++ -Wall -std=c++11 metaverse_run.cpp -o metaverse_run #include <iostream> #include <functional> typedef std::function<void(const int&)> result_handler; void execute_handler(result_handler handler) { handler(1); } class p2p { public: virtual void run(result_handler handler) { std::cout << "p2p::run: " << std::endl; execute_handler( std::bind(&p2p::handle_running, this, std::placeholders::_1, handler)); } private: void handle_running(const int& ec, result_handler handler) { std::cout << "p2p::handle_running: " << ec << std::endl; handler(ec); } }; class p2p_node : public p2p { public: virtual void run(result_handler handler) { std::cout << "p2p_node::run: " << std::endl; execute_handler( std::bind(&p2p_node::handle_running, this, std::placeholders::_1, handler)); } private: void handle_running(const int& ec, result_handler handler) { std::cout << "p2p_node::handle_running: " << ec << std::endl; p2p::run(handler); } }; class server_node : public p2p_node { public: virtual void run(result_handler handler) { std::cout << "server_node::run: " << std::endl; p2p_node::run( std::bind(&server_node::handle_running, this, std::placeholders::_1, handler)); } private: void handle_running(const int& ec, result_handler handler) { std::cout << "server_node::handle_running: " << ec << std::endl; handler(ec); } }; class executor { public: void start() { std::cout << "executor::start" << std::endl; server_node node; node.run( std::bind(&executor::handle_running, this, std::placeholders::_1)); } private: void handle_running(const int& ec) { std::cout << "executor::handle_running: " << ec << std::endl; } }; int main(){ executor exec; exec.start(); }
运行 ./metaverse_run 即可清楚地看到调用流程如下:
executor::start server_node::run: p2p_node::run: p2p_node::handle_running: 1 p2p::run: p2p::handle_running: 1 server_node::handle_running: 1 executor::handle_running: 1