排序服务为什么要使用Kafka
保证交易顺序
对于 fabric 中的每个channel,orderer将其映射到 kafka集群中的一个 topic ,即一个通道对应一个 topic,而每个 topic 只创建一个分区(partion)。简而言之,一个channel对应一个partition。
一般来说,创建Topic只指定1个partition,这样的坏处就是磨灭了kafka最优秀的特性。kafka本身适合与流式大数据量,要求高吞吐,对数据有序性要求不严格的场景。
但是,Fabric在此处使用Kafka反而是合适的,
Fabric包含了多个channel,每个channel对应一个partition。而Fabric官方预计实现的吞吐量是10w tps,一个简单的Kafka集群能够轻松达到300w tps。所以一个topic只包含一个partition,一个channel对应一个topic是合适的。又因为消息只有在单个分区里面是有序的,这样就保证了交易的顺序。
崩溃容错机制
使用kafka集群配置的原因也很简单,为orderer共识及排序服务提供足够的容错空间,当我们向peer节点提交Transaction的时候,peer节点会得到或返回(基于SDK)一个读写集结果,该结果会发送给orderer节点进行共识和排序。
此时如果orderer节点突然down掉,致使请求服务失效而引发的数据丢失等问题,且目前的sdk对orderer发送的Transaction的回调会占用极长的时间,当大批量数据导入的时候该回调可认为不可用。
因此,在部署生产环境时,需要对orderer进行容错处理,而所谓的容错即搭建一个orderer节点集群,该集群会依赖于kafka和zookeeper。
崩溃容错机制是通过在多个Kafka代理之间复制分区来实现的。因此如果一个代理由于软件或硬件故障挂掉,数据也不会丢失。当然接下来还需要一个领导-跟随机制,领导者持有分区,跟随者则进行分区的复制。当领导者挂掉后,会有某个跟随者转变为新的领导者。
ttc-x(time to cut)
排序服务的基本工作原理是这样的:
- 排序服务Client向OSN(orderer service node)发送交易;
- OSN节点对交易进行相关检查,符合条件之后会将交易发送给Kafka集群;
- OSN节点从Kafka集群拉取交易消息并对交易消息进行打包将打包之后的交易batch写入本地数据库;
- OSN节点按客户端Deliver请求从本地数据库读取区块返回;
关于ttc-x(结块)操作:为了提高 OSN 的效率,将kakfa的交易数据按照批次出块,这样可以减少验证次数,减少签名(因为签名比较费时)。但是按照数量出块,又会带来新的问题:因为出块是异步的,假设设置 batchSize=1000,orderering 需要发出1000个交易的batch,现在已经有了999个交易存储在内存中,只需要等待一个交易就可以生成新的区块,但是没有交易发往orderering了,这时候前面的999个交易就被动延迟了。这是不可接受的。
为了解决这个问题,引入了出块时间 batchTimeout,即用另外一个维度-时间去触发切块操作。这样既超时或者达到批次上限都会触发结块操作。因为不同的 OSN 之间都是通过kakfa集群获取数据,并没有直接的关联,而且OSN之间性能不一样,所以OSN的出块的时间很难一致。最终也会导致出块所包含的交易不一致,这也是不可以接受的。
Kafka在Fabric排序中存在的缺点
Kakfa的高性能对于Hyperledger Fabric有很大的帮助,多个排序节点通过Kafka实现同步,而Kafka本身并不是排序节点,它只是将排序节点通过流连接起来。虽然Kafka支持崩溃容错,它并不能提供对网络中恶意攻击的保护。需要一种拜占庭容错方案(BFT)才可以对抗恶意的攻击。
另外Kafka实现的集群太过于集中,需要有一个组织专门运行kafka集群,不符合区块链的最初设计理念。