五 共识
Table of Contents
概述
PoW, PoET, RBFT, ...
实现数据隔离的multi-channel
Ordering Service给客户端和Peer节点提供了一个共享通信Channel, 用来实现交易的广播服务. 客户端连接到channel上, 在通道上广播的消息最终会发送到channel内部的所有Peer节点.
Ordering Service支持multi-channel, 客户端连接到一个指定的channel上, 就可以发送和获取消息了. channel是相互隔离的, 客户端连接到一个channel的时候, 并不知道其他channel的存在, 这边所说的隔离, 并不针对Orderer. 客户端可以连接到多个channel. multi-channel的例子如下:
客户端获得背书节点返回的提案响应后, 构造交易, 提交给Orderer节点, Orderer节点 根据交易信息里提案的请求头确定channel信息
, 添加到对应的队列中进行排序, 生成区块后广播给该channel内的节点.
每个channel在节点上都有一个对应的账本.
排序服务的初始化
排序服务由多个Orderer节点组成, 每个Orderer启动的时候, 都需要一个创世区块
. 创世区块内包含的信息有:
- Orderer节点信息及MSP信息(管理员证书, 根证书, TLS根证书)
- 组织信息及MSP信息(管理员证书, 根证书, TLS根证书)
- 共识算法类型
- 区块配置信息
- 访问控制策略
configtxgen工具可以生成创世区块, configtxlator工具可以将区块转换成JSON格式.
创建channel
客户端可以通过SDK向Ordering Service发起创建channel的请求, 提交的内容是channtl配置交易
. configtxgen工具可以生成channel配置交易(同样, 可以转换成JSON格式的内容来查看).
Ordering Service接收到创建channel的请求, 会检查是否是配置交易, 检查的方法是查看ChannelHeader的类型是否为HeaderType_CONFIG_UPDATE(新建和更新channel都是这个类型), 然后Ordering Service重新生成一个配置交易, 修改交易类型为HeaderType_CONFIG. 新生成的交易会利用接收消息的Orderer的私钥重新进行签名, 然后添加到系统链的交易消息队列中进行处理.
创建一个channel就会有一个新的链. 客户端发起创建channel的请求, 创建成功后返回这个链的创世区块. 新链的创世区块中包含了channel配置交易的内容, 扩展了从系统链上保存的组织信息, Ordering Service信息等, 这样节点可以根据这个创世区块确定新链的标识, Orderer节点等, 而不用访问Orderer上的系统链.
更新channel
更新通道关键点
确定好需要修改的配置项, 离线修改后, 通过SDK或CLI发送给Orderer.
通道配置通过一个交易的形式存储在通道的配置区块中, 并且能够直接被修改. 但是, 在Fabric v1.0.0中, 还不能使用SDK来直接修改配置. configtxlator工具提供了一个API, 可以让SDK用户能够与这个API交互来更新配置.
更新通道的流程:
- SDK取出最新配置
- configtxlator工具将配置转化成我们能看懂的JSON文件
- 客户端编辑配置文件
- 使用configtxlator工具计算原有配置文件与现有配置文件的差异
- 客户端使用SDK提交配置和签名
注: 个人认为, 服务器与客户端的概念, 是相对的. 对于用户直接使用的程序来说, 服务器是使用SDK的应用程序, 对于使用SDK的应用程序来说, 服务器是Fabric.
configtxlator工具的使用
configtxlator --help
使用该命令了解configtxlator工具的使用.
启动configtxlator:
configtxlator start
加入channel
应用程序通过SDK或CLI向节点发送JoinChain请求, 参数为该通道的创世区块, 请求类型为HeaderType_CONFIG.
节点校验创世区块的合法性: 是否包含应用相关的配置项, 对提交者的身份进行认证和权限检查. 配置交易(创建通道所需的文件)要用客户端的私钥进行签名. 权限检查主要包含两点:
- 是否有权限向节点提交请求(即检查提交者的MSP是否与本地MSP相同)
- 是否满足加入通道请求的策略: 管理员权限才能提交加入通道请求, 管理员的证书配置在
$CORE_PEER_MSPCONFIGPATH/admincerts
目录下.
查询channel
channel信息是节点本地维护的, 有一个值为chain的映射表, 在节点启动或有新channel加入时, 会更新这个映射表. 映射表的键为channelId, 遍历就能返回节点所加入的所有channel. 在CLI中, 可以输入 peer channel list
来查看.
channel的配置也可查询, 如获取Orderer.
进入CLI, 获取channel配置:
peer channel fetch config config_block.pb -o orderer.example.com:7050 -c testchainid
可插拔的排序服务
目前只有两种排序服务: Solo, Kafka.
排序服务接口
排序服务的业务需要是可以实现不同的逻辑. 主要的接口有创建链, 处理链消息, 增加新的排序服务.
创建链的接口
type Consenter interface { HandleChain(support ConsenterSupport, metadata *cb.Metadata) (Chain, error) }
这个接口的作用是创建一个对链的引用, 用于提供资源. 其中, 第一个参数提供交易过滤, 交易切割, 区块签名等功能; 第二个参数是一个指针, 指针链账本中最后一个提交块ORDERER的存储元数据. 如果是创世区块(genesis.block), 由于没有存储元数据字段定义, 所以该值为nil.
当Orderer接收到创建通道的请求时, 会根据genesis.block中配置的通道类型来创建新的通道, 调用的是不同的通道的 HandleChain()
.
不同排序服务的区块元数据不同, 如对于Kafka服务, 元数据中要保存Kafka的最新偏移, 而Solo则不需要, 所以 排序服务不能动态切换
.
链消息处理的接口
排序服务接收到某个通道上的交易后, 会提交给链处理. 需要实现的接口如下:
type Chain interface { Enqueue(env *cb.Envelope) bool Errored() <- chan struct{} Halt() }
Enqueue()
: 成功接收消息时, 返回true, 否则返回false
Errored()
: 发生错误时, 返回报错的channel, 终止客户端的等待
Halt()
: 用于分配链的各种资源, 并保持相关的最新状态, 包括从排序服务中读取资源, 将消息传递给区块进行拆分, 将区块结果写入账本.
Chain接口: 接收交易请求, 进行排序, 生成最终的区块, 提供了可以提交消息进行排序的方法. 在实现这个接口时, 需要把排好的交易通过blockcutter.Receiver进行交易分割, 最后写到账本中. 交易的分割有两种模式:
- 交易先进入消息流中, 它在消息流中是有序的, 消息流中的交易分割到不同的区块里, 最后写入到账本中, solo和kafka都是这种模式.
- 交易分割到不同的区块中, 交易是有序的, 最后写入账本中, sbft是这种模式.
增加新的排序服务
排序服务需要配置文件的支持和配置文件参数的识别.
配置文件支持: 生成genesis.block的配置文件configtx.yaml中, Orderer.OrdererType可以指定排序服务类型. 假设添加了newconsenter.
配置文件参数识别: 在initializeMulti ChainManager的consenters中增加映射. consenters["newconsenter"] = newconsenter.New(). 然后在newconsenter里实现新的排序和共识算法.
Solo
创建链
利用Golang的并发机制, 内部构建一个接收消息的通道sendChan, 然后返回处理交易信息的链multichain.Chain:
func newChain (support multichain.ConsenterSupport) *chain { return &chain { batchTimeout: support.SharedConfig().BatchTimeout(), support: support, sendChain: make(chan *cb.Envelope), exitChan: make(chan struct{}), } }
其中, batchTimeout是最长区块生成间隔时间; support提供交易切割和生成区块的功能, exitChan是服务异常的终止信息.
接收交易请求
创建链以后, 通过 Start()
启动链的处理过程. 在Solo中, 就是启动一个线程循环的接收发送到sendChan通道的数据, 然后进行交易的切割和区块的写入.
排序服务接收到交易请求以后, 根据不同的排序服务类型提交给不同的链进行处理, 入口函数是 Enqueue
. Solo类型接收到消息以后, 发送给sendChan就结束了.
错误处理
排序服务内部如果出现异常, 会给exitChan发送消息, 外部程序可以读取Errored返回的通道, 进行异常处理.
Kafka
基于Kafka的排序服务, 利用Kafka作为交易的消息队列, 实现高吞吐量的数据分发. 每个通道都对应Kafka的一个Topic, 排序服务节点在不同阶段充当不同的角色.
- 接收交易阶段: 充当Kafka的生产者(producer)
- 消息处理阶段: 充当Kafka的消费者(consumer)
创建链
接收交易请求
错误处理
Kafka实践
节点数
Kafka节点数至少4个; Zookeeper节点数选奇数个, 至少为3.
创建genesis.block
修改configtx.yaml文件. 主要是修改Orderer.OrdererType, Orderer.kafka.Brokers, Orderer.AbsoluteMaxBytes三个选项.
Generated by Emacs 25.x(Org mode 8.x)
Copyright © 2014 - Pinvon - Powered by EGO