Pinvon's Blog

所见, 所闻, 所思, 所想

五 共识

概述

学习Fabric共识

PoW, PoET, RBFT, ...

实现数据隔离的multi-channel

Ordering Service给客户端和Peer节点提供了一个共享通信Channel, 用来实现交易的广播服务. 客户端连接到channel上, 在通道上广播的消息最终会发送到channel内部的所有Peer节点.

Ordering Service支持multi-channel, 客户端连接到一个指定的channel上, 就可以发送和获取消息了. channel是相互隔离的, 客户端连接到一个channel的时候, 并不知道其他channel的存在, 这边所说的隔离, 并不针对Orderer. 客户端可以连接到多个channel. multi-channel的例子如下:

45.png

客户端获得背书节点返回的提案响应后, 构造交易, 提交给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

可参考博客: 动态加入组织

大概内容如下:

系统启动和通道创建分别要依赖于创世区块和通道文件. 可以将通道文件使用configtxlator工具转换成我们能看懂的JSON格式, 进行编辑.

更新通道关键点

确定好需要修改的配置项, 离线修改后, 通过SDK或CLI发送给Orderer.

通道配置通过一个交易的形式存储在通道的配置区块中, 并且能够直接被修改. 但是, 在Fabric v1.0.0中, 还不能使用SDK来直接修改配置. configtxlator工具提供了一个API, 可以让SDK用户能够与这个API交互来更新配置.

更新通道的流程:

  1. SDK取出最新配置
  2. configtxlator工具将配置转化成我们能看懂的JSON文件
  3. 客户端编辑配置文件
  4. 使用configtxlator工具计算原有配置文件与现有配置文件的差异
  5. 客户端使用SDK提交配置和签名

注: 个人认为, 服务器与客户端的概念, 是相对的. 对于用户直接使用的程序来说, 服务器是使用SDK的应用程序, 对于使用SDK的应用程序来说, 服务器是Fabric.

configtxlator工具的使用

configtxlator --help

使用该命令了解configtxlator工具的使用.

启动configtxlator:

configtxlator start

加入channel

应用程序通过SDK或CLI向节点发送JoinChain请求, 参数为该通道的创世区块, 请求类型为HeaderType_CONFIG.

节点校验创世区块的合法性: 是否包含应用相关的配置项, 对提交者的身份进行认证和权限检查. 配置交易(创建通道所需的文件)要用客户端的私钥进行签名. 权限检查主要包含两点:

  1. 是否有权限向节点提交请求(即检查提交者的MSP是否与本地MSP相同)
  2. 是否满足加入通道请求的策略: 管理员权限才能提交加入通道请求, 管理员的证书配置在 $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进行交易分割, 最后写到账本中. 交易的分割有两种模式:

  1. 交易先进入消息流中, 它在消息流中是有序的, 消息流中的交易分割到不同的区块里, 最后写入到账本中, solo和kafka都是这种模式.
  2. 交易分割到不同的区块中, 交易是有序的, 最后写入账本中, sbft是这种模式.

增加新的排序服务

排序服务需要配置文件的支持和配置文件参数的识别.

配置文件支持: 生成genesis.block的配置文件configtx.yaml中, Orderer.OrdererType可以指定排序服务类型. 假设添加了newconsenter.

配置文件参数识别: 在initializeMulti ChainManager的consenters中增加映射. consenters["newconsenter"] = newconsenter.New(). 然后在newconsenter里实现新的排序和共识算法.

47.png

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, 排序服务节点在不同阶段充当不同的角色.

  1. 接收交易阶段: 充当Kafka的生产者(producer)
  2. 消息处理阶段: 充当Kafka的消费者(consumer)

创建链

接收交易请求

错误处理

Kafka实践

节点数

Kafka节点数至少4个; Zookeeper节点数选奇数个, 至少为3.

创建genesis.block

修改configtx.yaml文件. 主要是修改Orderer.OrdererType, Orderer.kafka.Brokers, Orderer.AbsoluteMaxBytes三个选项.

配置Kafka集群

连接Kafka节点出现异常时的重试设置

排序服务节点和Kafka节点之间的安全传输

Comments

使用 Disqus 评论
comments powered by Disqus