producer_plugin插件中的区块生产循环主函数schedule_production_loop,该函数的最主要目的是每次调用它会开启一个pending block,pending block打包交易为生产下一个block提供上下文,如果是当前生产者那么pending block最终成为block,反之则会在接收到一个block之后抛弃当前pending block,下面给出schedule_production_loop函数并提供详细注释
void producer_plugin_impl::schedule_production_loop() {
chain::controller& chain = app().get_plugin<chain_plugin>().chain();
// 每次调用 schedule_production_loop 之前设置的_timer会被取消
_timer.cancel();
std::weak_ptr<producer_plugin_impl> weak_this = shared_from_this();
bool last_block;
// 开启一个pending block并执行unapplied_transactions和scheduled_transactions
auto result = start_block(last_block);
// 执行unapplied_transactions和scheduled_transactions过程中遇到的错误
if (result == start_block_result::failed) {
elog("Failed to start a pending block, will try again later");
_timer.expires_from_now( boost::posix_time::microseconds( config::block_interval_us / 10 ));
// we failed to start a block, so try again later?
_timer.async_wait([weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) {
auto self = weak_this.lock();
if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) {
self->schedule_production_loop();
}
});
}
// 从start_block可以看到 now - chain.head_block_time() > 5 才会返回start_block_result::waiting,这种情况一般发生在区块没有
// 同步到最新,这种情况下只能等待直到区块同步到最新,注意这个时候 pending为空
else if (result == start_block_result::waiting) {
// nothing to do until more blocks arrive
elog("nothing to do until more blocks arrive");
}
// 轮到当前节点生产
else if (_pending_block_mode == pending_block_mode::producing) {
// we succeeded but block may be exhausted
static const boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
if (result == start_block_result::succeeded) {
// ship this block off no later than its deadline
//std::cout<<"expires_at: "<<epoch + boost::posix_time::microseconds(chain.pending_block_time().time_since_epoch().count() + (last_block ? _last_block_time_offset_us : _produce_time_offset_us))<<std::endl;
_timer.expires_at(epoch + boost::posix_time::microseconds(chain.pending_block_time().time_since_epoch().count() + (last_block ? _last_block_time_offset_us : _produce_time_offset_us)));
fc_dlog(_log, "Scheduling Block Production on Normal Block #${num} for ${time}", ("num", chain.pending_block_state()->block_num)("time",chain.pending_block_time()));
}
// start_block_result::exhausted,跳过当前生产时间进入下一个块时间
else {
auto expect_time = chain.pending_block_time() - fc::microseconds(config::block_interval_us);
// ship this block off up to 1 block time earlier or immediately
if (fc::time_point::now() >= expect_time) {
_timer.expires_from_now( boost::posix_time::microseconds( 0 ));
} else {
_timer.expires_at(epoch + boost::posix_time::microseconds(expect_time.time_since_epoch().count()));
}
fc_dlog(_log, "Scheduling Block Production on Exhausted Block #${num} immediately", ("num", chain.pending_block_state()->block_num));
}
_timer.async_wait([&chain,weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) {
auto self = weak_this.lock();
if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) {
// 醒来之后 maybe_produce_block
auto res = self->maybe_produce_block();
fc_dlog(_log, "Producing Block #${num} returned: ${res}", ("num", chain.pending_block_state()->block_num)("res", res) );
}
});
}
// 没轮到当前节点生产,但当前节点是生产者
else if (_pending_block_mode == pending_block_mode::speculating && !_producers.empty() && !production_disabled_by_policy()){
// if we have any producers then we should at least set a timer for our next available slot
optional<fc::time_point> wake_up_time;
for (const auto&p: _producers) {
// 计算下一个轮到当前节点生产的时间
auto next_producer_block_time = calculate_next_block_time(p);
if (next_producer_block_time) {
// 这里减去了0.5s是对的,这样醒来的时候正好进入上面那个条件_pending_block_mode == pending_block_mode::producing,否则
// 会错误一次块生产机会
auto producer_wake_up_time = *next_producer_block_time - fc::microseconds(config::block_interval_us);
if (wake_up_time) {
// wake up with a full block interval to the deadline
wake_up_time = std::min<fc::time_point>(*wake_up_time, producer_wake_up_time);
} else {
wake_up_time = producer_wake_up_time;
}
}
}
if (wake_up_time) {
fc_dlog(_log, "Specualtive Block Created; Scheduling Speculative/Production Change at ${time}", ("time", wake_up_time));
static const boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
_timer.expires_at(epoch + boost::posix_time::microseconds(wake_up_time->time_since_epoch().count()));
_timer.async_wait([weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) {
auto self = weak_this.lock();
if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) {
self->schedule_production_loop();
}
});
} else {
fc_dlog(_log, "Speculative Block Created; Not Scheduling Speculative/Production, no local producers had valid wake up times");
}
}
// 当前节点为非生产节点
else {
fc_dlog(_log, "Speculative Block Created");
}
}
该函数其实最重要的2个调用点:
1)on_incoming_block 接收到网络中其他生产者生产的一个block之后,抛弃当前pending block,把新接收到的block当成新的block,最终调用该函数重新开启一个pending block
2)schedule_production_loop自身的定时器,一般发生在当前节点轮到生产block的时候,每隔0.5s醒来commit block之后会调用schedule_production_loop
网友评论