概览
当排序节点通过RPC广播(Broadcast)接收到交易时 ,它会检查广播交易的客户端是否有权限去修改通道(channel)数据,然后将这些交易分发到Kafka适当的分区(partition)中。
每一个通道(channel)在Kafka中被映射到一个单独的单分区(partition)主题(topic)。 在这里 topic 与 partition 二者是一对一的关系
该分区也被排序节点所消费(consume),排序节点将接收到的交易分组写入到本地区块,将其保留在本地账本中,并通过Deliver RPC
提供给需要接收的客户端。
在这个过程中orderer节点充当了 "生产者" 和 "消费者" 两个角色
- 生产者:对发送者的权限进行检查,之后发送给Kafka中对应通道(channel)的主题(Topic)分区(partition)
- 消费者: 实时监听消息,并对消息进行后续处理,生成区块或者分割交易。
Orderer主要包含两个接口 :
Broadcast: 客户端发送交易请求到排序服务进行排序处理。
Deliver:客户端或者Peer从排序服务获取排序后的区块。
Kafka相关的配置信息解析
大写的字母表示的是yaml文件中environment部分的内容。
xxx.yyy.zzz这种表示方式是按照yaml格式的层级关系
Kafka集群的节点数量最少为4个,这样就可以容忍一个节点宕机。所有的通道能够继续读写,也可以创建新的通道。
ZooKeeper集群的节点数量可以是3、5或者7。它必须是一个奇数来避免分裂(split-brain)情景,大于1以避免单点故障。 超过7个ZooKeeper服务器则被认为是多余的。
Orderer相关配置
Kafka 相关信息被写在网络的初始区块中. 如果你使用 configtxgen 工具, 编辑 configtx.yaml 文件– 或者挑一个现成的系统通道的初始区块配置文件。
Orderer.OrdererType
:指定排序服务的类型Kafka
Orderer.BatchTimeout
: 设置区块最长的时间间隔
Orderer.BatchSize. MaxMessageCount
: 设定每个区块可以包含的最大交易数量
Orderer.BatchSize. PreferredMaxBytes
:首选的区块大小
通过参数
Orderer.Batchsize.PreferredMaxBytes
设置首选的区块大小. Kafka 处理相对较小的信息有更高的吞吐量; 针对小于 1 MiB 大小的值
Orderer.BatchSize.AbsoluteMaxBytes
: 设定区块的最大容量,不包含区块头。(会影响Kafka. Brokers的配置)
Orderer. Addresses
:IP:Port的格式
Orderer.Kafka.Brokers
:这是一个 Kafka的seed Brokers列表。里面至少要包含两个seed。
General.GenesisFile
或者ORDERER_GENERAL_GENESISFILE
:指定初始区块(genesis.block)的位置
例如如下配置内容:
Orderer: &OrdererDefaults
OrdererType: kafka
Addresses:
- orderer.example.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 10
AbsoluteMaxBytes: 98 MB
PreferredMaxBytes: 512 KB
Kafka:
Brokers:
- kafka0:9092
- kafka1:9092
- kafka2:9092
- kafka3:9092
使用 configtxgen 工具 创建初始区块. 设置是全局的设置, 也就是说这些设置的生效范围是网络中所有的排序节点. 记录下初始区块的位置.
channel创建时orderer与Kafka的交互过程
ORDERER_KAFKA_RETRY_SHORTINTERVAL=1s
:轮询间隔
ORDERER_KAFKA_RETRY_SHORTTOTAL=30s
:超时时间
Kafka.Retry 区域让你能够调整
metadata/producer/consumer
请求的频率以及socket的超时时间. (这些应该就是所有在 kafka 的生产者和消费者 中你需要的设置)
当一个 channel 被创建, 或当一个现有的 channel 被重新读取(刚启动 orderer 的情况), orderer 通过以下方式和 Kafka 集群进行交互
- 为 channel 对应的 Kafka 分区 创建一个 Kafka 生产者
- 通过生产者向这个分区发一个空的连接信息
- 为这个分区创建一个 Kafka 消费者.
- 如果任意步骤出错, 你可以调整其重复的频率.
这些步骤会在每一个
Kafka.Retry.ShortInterval
指定的时间间隔后进行重试Kafka.Retry.ShortTotal
次.
再以Kafka.Retry.LongInterval
规定的时间间隔重试Kafka.Retry.LongTotal
次直到成功.
需要注意的是 orderer 不能读写该 channel 的数据直到所有上述步骤都成功执行.
Kafka相关配置
适当配置你的Kafka集群. 确保每一个Kafka节点都配置了以下的值
KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE=false
数据一致性是区块链环境的关键. 我们不能选择不在同步副本集中的channel leader, 也不能冒风险去覆盖前一leader所产生的偏移量, 那样的结果就是重写orderers所产生的区块链数据. 所以设置为false
KAFKA_DEFAULT_REPLICATION_FACTOR = N
(N是一个待设定的数字)。
N 的值应该小于Kafka集群的数量。
参数 N 表示每个channel
的数据会复制到 N 个 broker 中. 这些是channel
同步副本集的候选. 不是所有 broker 都需要是随时可用的. N 值需要设置为绝对小于 Kafka集群的数量 , 因为channel的创建需要不少于 N 个broker是启动的. 所以如果设置N = Kafka集群的节点数量
, 一个 broker 宕机就意味着区块链网络不能再创建channel. 那么故障容错的排序服务也就不存在了.
KAFKA_MIN_INSYNC_REPLICAS = M
(M是一个待设定的数字)。
M 的值要大于 1 小于 N 。
数据至少复制到 M 个副本中,才被认为是有效同步。
如果 N - M 个设备宕机。还是可以正常工作。
如果可访问的数量小于M个, 则它会停止接受写入操作. 读操作可以正常运行. 当M个副本重新同步后,通道就可以再次变为可写入状态.
注意这个 M 和 N 的最小值是 2 和 3 。因为 Kafka集群的节点最小数量为 4 个。 1 < M < N < 4。
KAFKA_MESSAGE_MAX_BYTES :
消息的最大字节数
如果
Orderer.BatchSize.AbsoluteMaxBytes
的值设置为98。
则可将KAFKA_MESSAGE_MAX_BYTES
的值设置为99 * 1024 * 1024
KAFKA_REPLICA_FETCH_MAX_BYTES
的值也设置为99 * 1024 * 1024
KAFKA_REPLICA_FETCH_MAX_BYTES :
KAFKA_MESSAGE_MAX_BYTES
和KAFKA_REPLICA_FETCH_MAX_BYTES
的值需要大于Orderer.BatchSize.AbsoluteMaxBytes
的值. 再为区块头增加一些余量 -- 1 MiB 就足够了. 需要满足以下条件:
KAFKA_MESSAGE_MAX_BYTES
需要严格小于socket.request.max.bytes
, 这个值默认是100Mib。(如果你希望区块大于100MiB, 你需要去修改硬代码中的变量brokerConfig.Producer.MaxMessageBytes
, 代码位置是fabric/orderer/kafka/config.go
, 再重新编译代码, 不建议这么做)
services:
zookeeper:
image: hyperledger/fabric-zookeeper
restart: always
ports:
- '2181'
- '2888'
- '3888'
kafka:
image: hyperledger/fabric-kafka
restart: always
environment:
- KAFKA_MESSAGE_MAX_BYTES=103809024 # 99 * 1024 * 1024 B
- KAFKA_REPLICA_FETCH_MAX_BYTES=103809024 # 99 * 1024 * 1024 B
- KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE=false
ports:
- '9092'
orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer
environment:
- ORDERER_KAFKA_RETRY_SHORTINTERVAL=1s
- ORDERER_KAFKA_RETRY_SHORTTOTAL=30s
- ORDERER_KAFKA_VERBOSE=true
ports:
- 7050:7050
kafka0:
container_name: kafka0
extends:
file: base/docker-compose-base.yaml
service: kafka
environment:
- KAFKA_BROKER_ID=0
- KAFKA_MIN_INSYNC_REPLICAS=2
- KAFKA_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_ZOOKEEPER_CONNECT=zookeeper0:2181,zookeeper1:2181,zookeeper2:2181
depends_on:
- zookeeper0
- zookeeper1
- zookeeper2
kafka1:
container_name: kafka1
extends:
file: base/docker-compose-base.yaml
service: kafka
environment:
- KAFKA_BROKER_ID=1
- KAFKA_MIN_INSYNC_REPLICAS=2
- KAFKA_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_ZOOKEEPER_CONNECT=zookeeper0:2181,zookeeper1:2181,zookeeper2:2181
depends_on:
- zookeeper0
- zookeeper1
- zookeeper2
kafka2:
container_name: kafka2
extends:
file: base/docker-compose-base.yaml
service: kafka
environment:
- KAFKA_BROKER_ID=2
- KAFKA_MIN_INSYNC_REPLICAS=2
- KAFKA_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_ZOOKEEPER_CONNECT=zookeeper0:2181,zookeeper1:2181,zookeeper2:2181
depends_on:
- zookeeper0
- zookeeper1
- zookeeper2
kafka3:
container_name: kafka3
extends:
file: base/docker-compose-base.yaml
service: kafka
environment:
- KAFKA_BROKER_ID=3
- KAFKA_MIN_INSYNC_REPLICAS=2
- KAFKA_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_ZOOKEEPER_CONNECT=zookeeper0:2181,zookeeper1:2181,zookeeper2:2181
depends_on:
- zookeeper0
- zookeeper1
- zookeeper2
orderer.example.com:
extends:
file: base/docker-compose-base.yaml
service: orderer.example.com
container_name: orderer.example.com
depends_on:
- zookeeper0
- zookeeper1
- zookeeper2
- kafka0
- kafka1
- kafka2
- kafka3
调试
设置 orderer.yaml
文件中 General.LogLevel
为 DEBUG
和Kafka.Verbose
为true
建议以及注意事项
你可以使用环境变量重写设置.
你能够通过设置环境变量来重写Kafka
节点和Zookeeper
服务器的设置.
替换配置参数中的 点 为 下划线 – 例如:KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE=false
环境变量重写配置参数unclean.leader.election.enable
.
环境变量重写同样适用于排序节点的本地配置, 即orderer.yaml
中所能设置的. 例如ORDERER_KAFKA_RETRY_SHORTINTERVAL=1s
环境变量可以重写本地配置文件中的Orderer.Kafka.Retry.ShortInterval.
启动环境时,建议按照以下顺序启动:ZooKeeper 集群, Kafka 集群, 排序节点