EOS 代码分析 [1] —— AppBase

EOSIO/appbase,如项目介绍所说——

The AppBase library provides a basic framework for building applications from a set of plugins. AppBase manages the plugin life-cycle and ensures that all plugins are configured, initialized, started, and shutdown in the proper order.

是一个从一系列插件构建应用的基本框架。 EOS的大多应用都是基于这个框架来构建的。

AppBase是独立于EOS项目的一个独立项目,可以单独的编译,我们也可以利用这个框架构建自己的应用。

为了方便描述,我fork了一份代码,并在这个fork项目上添加了我阅读过程中的注释,这个fork的项目在这里:

andyzhshg/appbase

下文的介绍均基于AppBase提供的示例程序为基础进行说明,这个示例程序实现了一个net_plugin,这个net_plugin又有一个依赖项chain_plugin。这基本展示了AppBase使用中的方方面面。

0. 基本使用流程

一个基于AppBase的程序的基本使用流程如下:

  1. 注册插件,register_plugin
  2. 初始化,initialize
  3. 启动,startup
  4. 进入事件监听等待,exec
  5. 程序退出,shutdown

基本上就是这么简单,AppBase本身提供了一个基础进程环境,来使得用户的代码可以以插件的形式集成进来。

下面我们逐一解析一下这些步骤

1. 注册插件

application::register_plugin是一个模板函数,模板参数是要注册的插件的类型。

该函数首先通过插件的名称查找该插件是否已经注册过,如果注册过则直接返回,不会重复注册。插件名的名称是根据插件的类型推导得来的。

如果没有注册过,则new一个插件的对象,并将其记录进plugins这个列表中。

调用插件自身的register_dependencies来注册插件自身的依赖,插件的依赖也是插件。

2. 初始化

application::initialize函数的实现可以看到实际的初始化工作是在application::initialize_impl完成的。

其完成的主要工作是完成程序配置项的设置和根据配置项做初始化。

配置项的初始化部分,首先是调用插件的配置项设置 ,然后才是程序自身的配置项设置。配置项的处理是使用boost::program_options完成的。

配置项处理完成后,首先是根据配置进行application自身的初始化处理,然后是根据配置项进行插件的初始化处理

3. 启动

application::startup的过程很简单,就是逐一调用插件的startup函数,并处理异常。

因为application::startup本身不进行任何应用逻辑的处理,所有的应用逻辑都是插件来完成的,所以我们只要看一下插件是如何完成startup的。

我们发现,用户并不需要实现startup函数,而是实现一个plugin_requiresplugin_startup。而事实上如果我们观察示例程序,发现其并没有实现plugin_requires函数,这个函数事实上是实现了的,由一个APPBASE_PLUGIN_REQUIRES宏来实现,这个宏极大的简化了声明依赖的过程,只需要提供一个依赖的插件的类型的列表即可,我的代码注释中有对这个宏的简单解释,你也可是尝试展开这个宏。经过这个宏的简化,实际上必须有用户自己完成的只有plugin_startup这个函数了,这里给用户一个机会来完成插件自身在启动前要完成的工作。

需要注意的是,插件的调用顺序是与注册的顺序相同的。

4. 进入事件等待

application::exec函数启动一个boost::asio::io_service并监听了几个信号:SIGINT / SIGTERM / SIGPIPE,并阻塞在io_serv->run(); 这行代码,当进程收到这几个信号中的一个的时候,阻塞函数返回,并进入shutdown函数。

5. 程序退出

观察示例代码我们发现并没有显式调用application::shutdown函数,实际上这是由上一步骤中监听的事件来触发的,也就是类似CTRL+C这样的键盘时间或者kill之类的命令触发的。

在这个函数中,会逐一的调用插件的shutdown函数,时间上就是用户自己编写的plugin_shutdown函数。

需要注意的是,shutdown中调用插件的顺序是与startup的时候的顺序相反的,也就是注册的相反的顺序。

总结

综合上述的流程,我们发现基于AppBase编写一个程序主要的工作就是编写插件,而完成一个插件的流程就是如下简单几个步骤:

  1. appbase::plugin派生

  2. APPBASE_PLUGIN_REQUIRES宏来声明插件的依赖

  3. 实现plugin_initialize

  4. 实现plugin_startup

  5. 实现plugin_shutdown

对于AppBase的实现细节我的fork项目andyzhshg/appbase里的注释解释的比较详细了。其中有一部分是关于methodchannel的,AppBase本身的示例并没有演示用法,这篇文章没有解释,也许后续研究EOS的过程中我会回头解释这个设计。