元界(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