浅谈 fabric 出块机制和条件

作者 tinywell 日期 2020-03-08
浅谈 fabric 出块机制和条件

刚接触 fabric 的小伙伴时常会问的一个问题是:“fabric 多久出一个块”,而一个常见的回答是:“默认 2s”。抛开其严谨性,fabric 初学者一般会把这个作为一个常识性结论。

前两天一个小伙伴在进行 fabric 性能测试时,突然发现交易响应时间居然小于 2s。由于一个交易响应时间中主要为出块时间,他感觉这个好像跟常识不太对,会不会哪里出问题了,于是反馈给我。我告诉他,这说明你的性能测试运行的很好,已经将压力压上去了。至于为什么会这样,就需要展开聊一聊 fabric 的出块机制和相关的出块条件了。

在 fabric 中,一个区块结构 Block 的主要数据部分是由一个或多个交易(或者配置)数据结构 Envelope 组成。出块即是将一个或多个 Envelope 打包成一个 Block,这个工作由 orderer 完成。

当客户端将一个交易相关的数据组成的 Envelope 提交给 orderer 后,最终将被转给 blockcutter 按顺序缓存,满足条件后,提取出来进行区块打包。如何满足出块条件呢?这包含以下几个指标:

  • **BatchTimeout**:这个超时指的是,超过一定时间没有出块了,如果缓存里还有数据,那么就需要赶紧出块。所以 orderer 会维护一个以 BatchTimeout 为间隔的闹钟,定时检测缓存中是否还有未出块的数据,如果有就出块,否则等待下一次检测。因为这个条件的存在,一笔交易提交到 orderer 后,即使没有其他条件的触发,最晚也会在 BatchTimeout 时间后被打包出块,得到交易结果。这个参数是对时间的限制。
  • **MaxMessageCount**:代表一个块中最大可以包含的交易数量,当一笔新交易来了之后,如果加上缓存里的交易数量达到了这个条件,也会立即打包出块。这个参数是对数量的限制。
  • **PreferredMaxBytes**:这也是用来限制一个块的大小,正常情况下一个块中的交易数据大小加起来应小于等于这个参数,但是也有例外情况。两种情况:1.当一个新交易提交上来后,如果发现和缓存里的交易加起来超过了这个大小,则先将缓存里的交易出块,保证块大小小于 PreferredMaxBytes;2.当一个新交易提交上来之后,如果发现这个交易的大小已经超过 PreferredMaxBytes,则单独为这个交易打包出块,这样的块就超出了 PreferredMaxBytes 的限制。这个参数合适对大小的限制,但是并不严格。
  • **AbsoluteMaxBytes**:表示了一个块的最大大小,任何块任何情况下都不会允许超过这个大小,如果一个交易数据本身就超过了这个大小,则会被直接退回,没有机会参与打包。一般会大于上面的 PreferredMaxBytes 。这个参数是对大小的绝对限制。

基于这几个指标, orderer 会以两种形式进行出块条件的检测和出块:

  1. 定时任务触发:维护以 BatchTimeout 为间隔的闹钟,定时检测缓存中是否还有未出块的交易,如果有则打包出块;
  2. 新交易触发:新交易首先在转交给 blockcutter 之前会进行一系列校验,其中如果大小超过了 AbsoluteMaxBytes,则会报错,此交易不参与出块打包。符合要求的交易进入 blockcutter 后,以 PreferredMaxBytes 再次进行大小比较,如果超过这个大小,则先将缓存交易切块打包清空缓存,然后将此新交易单独打包。否则将此交易与缓存内的交易大小加起来再与 PreferredMaxBytes 进行比较,超过则缓存切块打包,新交易进入缓存。如果这些大小规则均不符合出块条件,则交易进入缓存,同时判断缓存内交易数量是否达到了 MaxMessageCount,满足则缓存内交易切块打包,否则此次没有打包。

这几个参数的默认配置如下:

1
2
3
4
5
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 500
AbsoluteMaxBytes: 10 MB
PreferredMaxBytes: 2 MB

回到文章开头,在一般情况下,低频小交易,触发出块条件最多的情况就是 BatchTimeout 的定时任务触发,而 BatchTimeout 的默认配置是 2s,所以我们常说 fabric 出块时间默认 2s,而这其实只是其中一种比较常见的情况。

根据我们实际的业务场景,如交易频率,交易数据大小等综合考虑,灵活调整出块相关的这几个参数,以得到合适的交易延迟和交易吞吐。