深度学习
排序服务在超级账本Fabric网络中起到十分核心的作用。所有交易在发送到网络中交由Committer进行验证接受之前,需要先经过排序服务进行全局排序。排序服务提供了原子广播排序功能。
在目前架构中,排序服务的功能被抽取出来,作为单独的fabric-orderer模块来实现,代码主要在fabric/orderer目录下。
排序服务主要由三部分组成:gRPC协议对外提供服务接口;账本组件网络中每个应用通道维护区块链结构,排序插件跟不同类型的排序后端打交道,如图12-26所示。
图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结构来对通道配置更新交易进行处理。
图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)等方法。
图12-28 multiLedger结构和方法
12.7.3 通道配置更新
对通道配置的更新主要通过Processor结构(实现在orderer/configupdate /configupdate.go文件)来完成,该结构主要提供了Process(envConfigUpdate*cb.Envelope) (*cb.Envelope,error)方法。
相关的数据结构如图12-29所示。
图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结 构。
目前,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中。
图12-31 Chain接口和实现
goroutine检查有新交易消息到达后会通过blockcutter.receiver结构(实现代码在 orderer/common/blockcutter/blockcutter.go文件中)的Ordered()方法进行排序。如果积累的交易消息足 够多,或者发生超时,则调用blockcutter.receiver结构的Cut()方法进行切分为区块,并在本地创建新的区块写到账本结构中。
solo.chain结构如图12-32所示。
图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()切割出新区块,并写入本地账本结构。
图12-33 Kafka排序后端的Chain结构
收到Regular消息则调用Ordered()进行排序,看是否满足生成区块的条件。满足则也生成新的区块,写入本地账本结构。
本章小结
本章剖析了超级账本Fabric项目的架构与设计,包括核心的概念和组件功能、通信协议以及关键的权限管理、链码设计、排序服务等。这些设计来自很多一线企业的区块链实现和应用经验,并融合了来自社区的众多区块链和分布式账本技术专家的论证。
从这些设计中,读者可以体会到Fabric项目针对联盟链的特定场景进行了诸多的优化。包括利用基于数字证书的权限机 制,可以满足现实世界中不同企业、组织、部门之间进行业务交互的需求,进行分层的权限管控;可扩展的共识机制,可以满足不同信任级别的交易场景,消除网络 中性能瓶颈;解耦交易的背书和执行阶段,通过不同角色节点来支持不同负载下的灵活部署;以及为了更好支持主流开发生态,遵循了诸多来自业界的实践规范,并 采用了来自开源界的标准化组件。
未来,Fabric项目还将在共识、SDK、数据隐私性保护、交易证书等方面进行进一步的增强,打造更为安全、可靠,并且易用的开源分布式账本实现,以满足商业场景下复杂多变的应用需求。
来源:我是码农,转载请保留出处和链接!
本文链接:http://www.54manong.com/?id=899
微信号:qq444848023 QQ号:444848023
加入【我是码农】QQ群:864689844(加群验证:我是码农)
全站首页 | 数据结构 | 区块链| 大数据 | 机器学习 | 物联网和云计算 | 面试笔试
var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1276413723'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s23.cnzz.com/z_stat.php%3Fid%3D1276413723%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));本站资源大部分来自互联网,版权归原作者所有!
评论专区