12.7 排序服务

排序服务在超级账本Fabric网络中起到十分核心的作用。所有交易在发送到网络中交由Committer进行验证接受之前,需要先经过排序服务进行全局排序。排序服务提供了原子广播排序功能。

在目前架构中,排序服务的功能被抽取出来,作为单独的fabric-orderer模块来实现,代码主要在fabric/orderer目录下。

排序服务主要由三部分组成:gRPC协议对外提供服务接口;账本组件网络中每个应用通道维护区块链结构,排序插件跟不同类型的排序后端打交道,如图12-26所示。

image.png

图12-26 Orderer主要结构

12.7.1 gRPC服务接口

Orderer通过gRPC接口提供了对外的调用服务,主要包括两个接口:Broadcast(srv ab.AtomicBroadcast_BroadcastServer)error和Deliver(srv ab.AtomicBroadcast_DeliverServer)error。其中:

·Broadcast(srv ab.AtomicBroadcast_BroadcastServer)error:意味着客户端发送交易请求到排序服务进行排序处理。gRPC服务接口地址是/orderer.AtomicBroadcast/Broadcast;

·Deliver(srv ab.AtomicBroadcast_DeliverServer)error:意味着客户端或Peer从排序服务获取排序后的区块(批量交易)。gRPC服务接口地址是/orderer.AtomicBroadcast/Deliver。

实现上,Orderer通过server结构(实现AtomicBroadcast_BroadcastServer接 口,代码在orderer/server.go文件)来作为入口结构,封装了handlerImpl和deliverserver两个句柄结构(代码在 orderer/common/broadcast,deliver两个包中)来分别处理Broadcast(srv ab.AtomicBroadcast_BroadcastServer)error和Deliver(srv ab.AtomicBroadcast_DeliverServer)error两个对外的接口。

这些结构和关键方法如图12-27所示。

其中,比较关键的是通过multiLedger结构来管理网络中的链和账本结构;通过Processor结构来对通道配置更新交易进行处理。

image.png

图12-27 Orderer主服务结构和方法

12.7.2 链和账本管理

Orderer节点本地需要维护网络中的账本结构。服务启动后会默认从本地文件进行查找和加载。其中,账本结构支持三种实现类型:

·ram:存放近期若干区块到内存中。保存的近期区块个数由配置$ORDERER_RAML-EDGER_HISTORYSIZE指定(可以通过命令行、环境变量、配置文件等方式指定);

·file:存放区块记录到本地文件系统。当$ORDERER_FILELEDGER_LOCATION有配置时,默 认是$ORDERER_FILELEDGER_LOCATION/chains下;否则在临时目录下创 建$ORDERER_FILELEDGER_PREFIX目录;

·json:存放区块记录到本地文件系统(存储格式为json)。 当$ORDERER_FILELEDGER_LOCATION有配置时,默认是$ORDERER_FILELEDGER_LOCATION/chains 下;否则在临时目录下创建$ORDERER_FILELEDGER_PREFIX目录。

以file类型账本为例,其初始化后本地路径结构可能如下所示:


 

/var/hyperledger/production/orderer/
|-- chains
|   `-- testchainid
|       `-- blockfile_000000
`-- index
   |-- 000001.log
   |-- CURRENT
   |-- LOCK
   |-- LOG
   `-- MANIFEST-000000


注意,Orderer中账本结构实际上维护的主要还是区块链结构,不包括状态数据库(Peer节点中会维护)。

链和账本结构的维护通过核心的multiLedger结构(实现Manager接口,代码在orderer/multichain/manager.go文件)来实现。

其中成员包括对本地链结构进行管理的结构chains和systemChannel、对后端共识插件的调用 consenters、对本地账本结构进行维护的三种类型的账本,以及用于签名的签名结构signer。方法则包括获取ChainSupport结构的 GetChain()、获取系统通道ID的System-ChannelID(),以及生成配置管理器的NewChannelConfig()方法。

这些结构和关键方法如图12-28所示,各个类型账本都实现了Append(blockcb.Block)、Height()、Iterator(startPositionab.SeekPosition)等方法。

image.png

图12-28 multiLedger结构和方法

12.7.3 通道配置更新

对通道配置的更新主要通过Processor结构(实现在orderer/configupdate /configupdate.go文件)来完成,该结构主要提供了Process(envConfigUpdate*cb.Envelope) (*cb.Envelope,error)方法。

相关的数据结构如图12-29所示。

image.png

图12-29 Processor结构和方法

其中,Process()方法接收一个CONFIG_UPDATE类型的Envelope结构消息,根据请求类型(新建通道或更新配置),将其转换为新建应用通道的请求,或者转换为对通道进行配置更改的请求。

主要过程包括如下步骤:

1)从请求中提取channelID,检查本地是否存在对应的链结构。

2)如果channelID对应的链在本地存在,则意味着这是一个对已有通道进行配置更新的请求。调用 multiLedger.chainSupport结构的ProposeConfigUpdate(env*cb.Envelope) (*cb.ConfigEnvelope,error)方法进行处理。实际上,最终调用的是common.configtx包中config- Manager结构的对应方法。

该方法先将Envelope结构中的ConfigUpdateEnvelope内容取出,之间进行权限检查,最后通过 processConfig(channelGroup*cb.ConfigGroup)(*configResult,error)方法进行处理。 Processer拿到处理后的ConfigEnvelope消息,创建并返回一个带有签名的Envelope结构。

3)如果channelID对应的链在本地不存在,则意味着这是一个新建通道的请求。调用 multiLedger.chainSupport结构的NewChannelConfig(envConfigUpdate*cb.Envelope) (configtxapi.Manager,error)方法进行处理。

该方法利用传入的参数创建一个CONFIG类型的Envelope结构并对其签名,并调用 common.configtx包中的NewManagerImpl(envConfig*cb.Envelope,initializer api.Initializer,callOnUpdate[]func(api.Manager))(api.Manager,error)方法创建并 返回一个configManager结构。

该结构进一步调用其ProposeConfigUpdate(configtx*cb.Envelope) (*cb.ConfigEnvelope,error)方法进行处理,得到ConfigEnvelope结构。之后将ConfigEnvelope结构封 装为CONFIG类型的Envelope结构并进行签名。最后,将得到的结构作为数据,封装为ORDERER_TRANSACTION类型的 Envelope结构并进行签名并返回。

12.7.4 共识插件

Orderer在共识上采用了可拔插的架构设计,将共识扔给后端插件完成。对于共识插件来说,接受交易信息进行排序,然后决定什么时候(取决于超时时间配置和打包尺寸限制)对交易进行切割并打包,打包后返回批量交易。

目前,Orderer模块中包括三种共识插件:

·Solo:单节点的排序功能。试验性质,不具备可扩展性和容错,不能在生产环境中使用;

·Kafka:基于Kafka集群的排序实现。支持CFT容错,支持可持久化和可扩展性,可在生产环境中使用;

·SBFT:支持BFT容错的排序实现,目前尚未完成。

这些后端需要实现Consenter接口。Consenter接口主要提供了HandleChain(support Consenter-Support,metadata*cb.Metadata)(Chain,error)方法,用来返回一个所对应的Chain结 构。

image.png

目前,Consenter接口主要包括两种实现:solo.consenter(实现代码在orderer/solo /consensus.go文件中)和kafka.consenterImpl(实现代码在orderer/kafka/consenter.go文件 中)。Consenter接口和实现如图12-30所示。

返回的Chain结构也对应包括两种实现:solo.chain(实现代码在orderer/solo /consensus.go文件中)和kafka.chainImpl(实现代码在orderer/kafka/chain.go文件中)。Chain接 口和实现如图12-31所示。

Chain接口是排序过程中十分重要的结构,其对应的Start()方法在Orderer服务启动后执行初始化过程中会被调用(相关代码在orderer/multichain/manager.go文件中)。

1.Solo排序后端

Solo排序后端在整个Orderer启动后初始化时候会创建一个solo.chain结构,并调用其Start()方 法,该方法会创建一个goroutine(执行main()方法),在后台一直轮询sendChan中是否有新的消息到达,或者是否超时 ($CONFIGTX_ORDERER_BATCHTIMEOUT)。

新消息到达后会通过Enqueue()方法,写入到sendChan中。

image.png

图12-31 Chain接口和实现

goroutine检查有新交易消息到达后会通过blockcutter.receiver结构(实现代码在 orderer/common/blockcutter/blockcutter.go文件中)的Ordered()方法进行排序。如果积累的交易消息足 够多,或者发生超时,则调用blockcutter.receiver结构的Cut()方法进行切分为区块,并在本地创建新的区块写到账本结构中。

solo.chain结构如图12-32所示。

image.png

图12-32 Solo排序后端的Chain结构

2.Kafka排序后端

Kafka排序后端在整个Orderer启动后初始化时候会创建一个kafka.chainImpl结构,并调用其Start()方法,该方法同样会创建一个goroutine(执行startThread()方法)。

与solo情况不同的是,Kafka排序后端是利用github.com/Shopify/sarama包来询问 Kafka集群中是否有新的消息到达,或者是否超时($CONFIGTX_ORDERER_BATCHTIMEOUT)。此时Orderer组件本身起到 代理的角色,很容易扩展到多个实例。

kafka.chainImpl结构中包括一个producer成员(负责往Kafka集群发送消息)、一个 parent-Consumer成员(管理各分区的consumer),以及一个channelConsumer成员(分区consumer,从指定的 topic和分区获取数据),如图12-33所示。

主要处理过程与solo插件类似。kafka.chainImpl结构的 processMessagesToBlocks()方法(位于orderer/kafka/chain.go文件)核心也是一个轮询过程,主要通过 channelConsumer来获取来自Kafka集群的消息。核心消息包括TimeToCut、Regular两种。

收到TimeToCut消息意味着对交易进行切割,调用Cut()切割出新区块,并写入本地账本结构。

image.png

图12-33 Kafka排序后端的Chain结构

收到Regular消息则调用Ordered()进行排序,看是否满足生成区块的条件。满足则也生成新的区块,写入本地账本结构。

本章小结

本章剖析了超级账本Fabric项目的架构与设计,包括核心的概念和组件功能、通信协议以及关键的权限管理、链码设计、排序服务等。这些设计来自很多一线企业的区块链实现和应用经验,并融合了来自社区的众多区块链和分布式账本技术专家的论证。

从这些设计中,读者可以体会到Fabric项目针对联盟链的特定场景进行了诸多的优化。包括利用基于数字证书的权限机 制,可以满足现实世界中不同企业、组织、部门之间进行业务交互的需求,进行分层的权限管控;可扩展的共识机制,可以满足不同信任级别的交易场景,消除网络 中性能瓶颈;解耦交易的背书和执行阶段,通过不同角色节点来支持不同负载下的灵活部署;以及为了更好支持主流开发生态,遵循了诸多来自业界的实践规范,并 采用了来自开源界的标准化组件。

未来,Fabric项目还将在共识、SDK、数据隐私性保护、交易证书等方面进行进一步的增强,打造更为安全、可靠,并且易用的开源分布式账本实现,以满足商业场景下复杂多变的应用需求。


来源:我是码农,转载请保留出处和链接!

本文链接:http://www.54manong.com/?id=899

'); (window.slotbydup = window.slotbydup || []).push({ id: "u3646208", container: s }); })();
'); (window.slotbydup = window.slotbydup || []).push({ id: "u3646147", container: s }); })();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值