Controller:EOS区块链核心控制器 (7)

同步快照数据的操作是在controller的startup周期中执行的,根据传入的snapshot,会调整区块链的基于block_log的不可逆日志数据,基于chainbase的状态主库数据。在controller的startup完毕后,可以保证三者数据的健康同步。

在chain_plugin的插件配置项中有一个“snapshot”的参数,该配置项可以指定读取的快照文件。几个关键校验:

注意不能同时配置“genesis-json”和“genesis-timestamp”两项,因为快照中已经存在这两项的值,会发生冲突。

不能存在已有状态文件data/state/shared_memory.bin,因为快照只能被用来初始化一个空的状态数据库。

校验block_log日志中不可逆区块的创世块是否与快照中的保持一致。

参数设置完毕,在chain_plugin的startup阶段,会检查快照地址,如果存在,则会带上该快照文件启动链。

if (my->snapshot_path) { auto infile = std::ifstream(my->snapshot_path->generic_string(), (std::ios::in | std::ios::binary)); auto reader = std::make_shared<istream_snapshot_reader>(infile); my->chain->startup(reader);// 带上该快照文件启动链。 infile.close(); }

my->chain的类型是fc::optional<controller>,所以会执行controller的startup函数,这样就与上面的流程挂钩了,形成了一个完整的逻辑闭环。

4. controller::write_snapshot void controller::write_snapshot( const snapshot_writer_ptr& snapshot ) const { // 写入快照时,不允许存在pending区块。 EOS_ASSERT( !my->pending, block_validate_exception, "cannot take a consistent snapshot with a pending block" ); return my->add_to_snapshot(snapshot); }

调用add_to_snapshot函数。

void add_to_snapshot( const snapshot_writer_ptr& snapshot ) const { snapshot->write_section<chain_snapshot_header>([this]( auto &section ){ section.add_row(chain_snapshot_header(), db);// 向快照中写入快照头数据 }); snapshot->write_section<genesis_state>([this]( auto &section ){ section.add_row(conf.genesis, db);// 向快照中写入创世块数据 }); snapshot->write_section<block_state>([this]( auto &section ){ section.template add_row<block_header_state>(*fork_db.head(), db);// 向快照中写入头块区块头数据。 }); controller_index_set::walk_indices([this, &snapshot]( auto utils ){ using value_t = typename decltype(utils)::index_t::value_type; if (std::is_same<value_t, table_id_object>::value) {// 跳过table_id_object(内联的合同表格部分) return; } snapshot->write_section<value_t>([this]( auto& section ){ // 遍历主库db区块。 decltype(utils)::walk(db, [this, &section]( const auto &row ) { section.add_row(row, db); // 向快照中逐行写入快照 }); }); }); add_contract_tables_to_snapshot(snapshot);// 向快照中写入合约数据 authorization.add_to_snapshot(snapshot);// 向快照中写入认证数据 resource_limits.add_to_snapshot(snapshot);// 向快照中写入资源限制数据 } 5. producer_plugin的create_snapshot()功能

controller::write_snapshot函数在外部由producer_plugin所调用。producer_plugin通过rpc api接口create_snapshot对外提供了创建快照的功能。这个功能无疑是非常实用的,可以为生产者提供快速数据备份的能力,为整个EOS区块链的运维工作增加了健壮性。producer_plugin的具体的实现代码:

producer_plugin::snapshot_information producer_plugin::create_snapshot() const { chain::controller& chain = app().get_plugin<chain_plugin>().chain();// 获取chain_plugin的插件实例 auto reschedule = fc::make_scoped_exit([this](){// 获取生产者出块计划 my->schedule_production_loop(); }); if (chain.pending_block_state()) {// 快照大忌:如果有pending块,不可生成快照。 // abort the pending block chain.abort_block();// 将pending块干掉 } else { reschedule.cancel();// 无pending块,则取消出块计划。 } // 开始写快照。 auto head_id = chain.head_block_id(); // 快照目录:可通过配置producer_plugin的snapshots-dir项来指定快照目录,会在节点数据目录下生成该快照目录,如果未特殊指定,默认目录名字为“snapshots” // 在快照目录下生成格式为“snapshot-${id}.bin”的快照文件。id是当前链的头块id std::string snapshot_path = (my->_snapshots_dir / fc::format_string("snapshot-${id}.bin", fc::mutable_variant_object()("id", head_id))).generic_string(); EOS_ASSERT( !fc::is_regular_file(snapshot_path), snapshot_exists_exception, "snapshot named ${name} already exists", ("name", snapshot_path)); auto snap_out = std::ofstream(snapshot_path, (std::ios::out | std::ios::binary));// 构造快照文件输出流 auto writer = std::make_shared<ostream_snapshot_writer>(snap_out);// 构造快照写入器 chain.write_snapshot(writer);// 备份当前链写入快照 // 资源释放。 writer->finalize(); snap_out.flush(); snap_out.close(); return {head_id, snapshot_path};// 返回快照文件路径 }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyyyfy.html