Fabric 0.6与1.0+
Fabric结构
Fabric 0.6的特点
- 结构简单: 应用-成员管理-Peer的三角形关系,主要业务功能全部集中于Peer节点;
- 架构问题:由于peer节点承担了太多的功能,所以带来扩展性、可维护性、安全性、业务隔离等方面的诸多问题,所以0.6版本在推出后,并没有大规模被行业使用,只是在一些零星的案例中进行业务验证
Fabric 1.0
- 分拆Peer的功能,将Blockchain的数据维护和共识服务进行分离,共识服务从Peer节点中完全分离出来,独立为Orderer节点提供共识服务;
- 基于新的架构,实现多通道(channel)的结构,实现了更为灵活的业务适应性(业务隔离、安全性等方面)
- 支持更强的配置功能和策略管理功能,进一步增强系统的灵活性和适应性;
Fabric结构理解
交易流程(数据传输流程)
在交代Fabric各个关键之前,先理清发起一笔交易需要经历的整个数据流程是什么。
总体流程如下图所示:
生命周期中的流程
完整生命周期
流程步骤:
-
应用程序通过SDK发送请求到Peer节点(一个或多个) 即发起交易
- 客户A发起交易请求:合约设置的背书策略规定所有交易需要经过两个Peer节点的签名背书,因此请求需要被同时发往Peer A和Peer B.
- 客户端应用程序利用任意SDK(nodeJS,java,python)构造交易提案。该提案是一个调用智能合约功能函数的请求,用来确认哪些数据可以读取或写入账本(即更新资产的Key/Value)。
- SDK将交易提案打包为可识别的格式(如gRPC上的protocol buffer),并使用用户的加密凭证为该交易提案生成唯一的签名。
-
peer节点(Endorser节点)通过chaincode分别执行交易,但是并不将执行结果提交到本地的账本中 (可以认为是模拟执行,交易处于挂起状态,放置于候选池)
- 参与背书的peer将执行结果返回给应用程序(其中包括自身对背书结果的签名)
- 背书节点(使用MSP)验证签名(ProcessPropsal()->preProcess()-->Verify()验证签名)并确定提交者是否有权执行操作(使用通道的ACL)。
- 背书节点将交易提案的参数作为输入,在当前状态数据库上执行交易,生成包含执行返回值、读操作集合和写操作集合的交易结果(此时不会更新账本),这些值的集合、背书节点的签名和背书结果(YES / NO)作为“提案的结果”返回给SDK
- SDK解析这些信息判断是否应用于后续的交易。
-
应用程序收集背书结果并将结果提交给Ordering服务节点
- 应用程序(SDK)验证背书节点签名,并比较各节点返回的提案结果,判断提案结果是否一致以及是否参照指定的背书策略执行。
- 应用程序(SDK)将交易提案和结果以消息形式广播到排序服务(Orderers/Consenter)。交易包含读/写操作集合、背书节点的签名和通道ID。
- 排序服务不读取交易的详细信息,它从整个区块链网络接收交易信息,按通道分类进行排序,并为每个通道创建包含交易的区块。
-
Ordering服务节点执行共识过程并生成block,通过消息通道发布给Peer节点(所有节点包括committer, submitter, endorser),由peer节点各自验证交易并提交到本地的ledger中(包括state状态的变化)
- 排序服务将区块发送到通道上的所有节点,所有交易需要被验证,确保满足背书策略
- 同时,需要确保全部读操作集合在交易生成之后,账本上的状态值没有改变。
- 经过验证,区块中的交易会被标记为有效或无效,通过Event通知客户端
-
所有节点账本更新
- 所有通道上的区块链节点将新区块加入区块链,并且对于所有有效的交易,将写操作集合提交更新到状态数据库中。
- 节点通过事件(Event)通知客户端交易是否已被加入区块链、以及交易是否有效。
Fabric概念结构
可以由下面的图大致概括:
(Peers + Orderers + Channels)
- Orderers: 即Consenter,共识插件,提供共识服务的网络节点,负责接受交易信息进行排序,以及对交易进行切割并打包,打包后返回批量交易。例如,使用Kafka或PBFT(SBFT),单节点使用solo单节点。
- Peers: 维护账本的网络节点,通常在Hyperledger Fabric架构中存在各种角色,如endorser和committer。
- 通道:通道是有共识服务(ordering)提供的一种通讯机制,类似于消息系统中的发布-订阅(PUB/SUB)中的topic;基于这种发布-订阅关系,将peer和orderer连接在一起,形成一个个具有保密性的通讯链路(虚拟),实现了业务隔离的要求;通道也与账本(ledger)-状态(worldstate)紧密相关; peer可以在订阅多个通道,并且只能访问订阅通道上的交易;且通道上的数据仅与peer有关,与order无关。
- 账本:账本保存Orders提交经节点确认的交易记录。
- 成员:访问和使用账本的网络节点。
- 成员管理: 每个membership(MSP组织)可以有自己的fabric-ca作为第三方认证机构,与背书策略对应。成员都需要在MSP中注册
- 链:基本上,一个链由1个通道+ 1个账本+ N个成员组成。非链的成员无法访问该链上的交易。链的成员可以由应用程序动态指定。
我们这时候再看一下前面的流程图,回忆一下交易经过了哪些节点
SDK --> Commiters --> Endorsers --> SDK --> Orderers --> Commiters&Endorsers --> SDK
可参考下图:
在节点Peers(Committer)提交到Endorsers时,会根据交易的需求,要求一部分背书节点进行交易所有权验证,背书签名并返回背书结果。
下面我们了解一下背书策略:
背书策略与设计
背书策略内容
背书策略,用于指示区块链节点交易验证的规则。作为交易验证流程的一部分,当背书节点收到一个交易请求的时候, 该节点会调用 VSCC (验证用途的系统合约程序) 并与执行交易的合约相关联。
为了确定交易的有效性,一个交易应该包含来自尽可能多的背书节点的一个或多个背书。VSCC用于判定下面的内容:
- 所有背书是有效的 (即它们是来自预期消息上的有效证书的有效签名)
- 得到一定数量的背书
- 背书来自预期的来源(指定背书节点)
背书策略设计
背书策略有两个主要组成部分:
- 主体principal
- 阀门threshold gate
P 标识期望背书的区块链节点,T有两个输入参数:整数t(背书数量)和n (背书节点列表),即满足t的条件,并得到n的背书。
例如:
- T(2, 'A', 'B', 'C') 请求来自'A'、'B'、'C'的任意2个背书节点的签名
- T(1, 'A', T(2, 'B', 'C')) 请求来自A或来自B和C中的一个签名
命令行下的背书策略语法
在Fabric CLI中,使用了一种简单的boolean表达式来解释Endorse节点的背书策略。
Fabric 1.0使用MSP(成员管理服务)来描述主体principal,该MSP用于验证签名者的身份以及签名者在该MSP内所具有的权限。
目前支持两种角色:成员和管理员。主体Principals的通用表现形式是MSP.ROLE,其中MSP是指MSP 的ID,ROLE是 member 或admin。 一个有效主体的示例是“Org0.admin”(Org0 MSP的任意管理员)或“Org1.member”(Org1 MSP的任意成员)。
示例:
命令行语法是这样的:
EXPR(E[, E...])
其中EXPR可以是AND或OR,代表两个boolean表达式,E是主体或对EXPR的另一个嵌套调用。
例如:
AND('Org1.member', 'Org2.member', 'Org3.member') 请求三个背书节点的签名
OR('Org1.member', 'Org2.member') 请求两个背书节点中的任意一个的签名
OR('Org1.member', AND('Org2.member', 'Org3.member'))
请求来自Org1 MSP成员或来自Org2 MSP成员和来自Org3 MSP成员的任意一个签名
指定智能合约的背书策略
部署合约的开发人员可以指定背书策略来验证执行的合约。
注,默认策略需要来自DEFAULT MSP成员的一个签名。如果未在CLI中指定策略,则默认使用此选项。
背书策略可以在部署合约时使用"-P选项"指定,后面跟策略内容。
例如:
peer chaincode deploy -C testchainid -n mycc -p http://github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -c '{"Args":["init","a","100","b","200"]}' -P "AND('Org1.member', 'Org2.member')"
执行这条命令将在testchainid这条链上使用背书策略AND('Org1.member', 'Org2.member').部署智能合约mycc
Orderers共识(Consenter)
共识机制
Fabric 0.6采用的是PBFT共识机制,但已经暂时取消该机制改为了Kafka共识。原因如下:
- 交易性能达不到要求
- Fabric 面向的联盟链环境中,因为节点都是有准入控制的,拜赞庭容错的需求不是很强烈,反而是并发性能最重要
在测试开发中较多使用SOLO共识机制
SOLO机制是一个非常容易部署的非生产环境的共识排序节点。由一个为所有客户服务的单一节点组成,所以不需要“共识”,因为有一个中央权威机构。相应地没有高可用性或可扩展性。这使得独立开发和测试很理想,但不适合生产环境部署。order-solo模式作为单节点通信模式,所有从peer收到的消息都在本节点进行排序与生成数据块
在Fabric 1.0往后版本中,采用的是Kafka共识机制,将来或采用SBFT(简化拜占庭容错共识)。
基于 Kafka 实现的共识
一个共识集群由多个 orderer 节点(OSN)和一个 kafka 集群组成。orderer 之间并不直接通信,他们仅仅和 Kafka 集群通信。
在 orderer 的实现里,通道(Channel)在 kafka 中是以 主题topic 的形式隔离。
每个 orderer 内部,针对每个通道都会建立与 kafka 集群对应 topic 的生产者及消费者。生产者将 orderer 节点收到的交易发送到 kafka 集群进行排序,在生产的同时,消费者也同步消费排序后的交易。
如何鉴别某个交易属于哪个区块
Fabric 的区块结块由两个条件决定,区块交易量和区块时间间隔。
- 当配置的交易量达到阈值时,无论是否达到时间间隔,都会触发结块操作;
- 另一方面,如果触发了设置的时间间隔阈值,只要有交易就会触发结块操作,也就是说 fabric 中不会有空块。
- 结块操作是由 orderer 节点中的 kafka 生产者发送一条 TTC-X(Time to cut block x)消息到 kafka 集群,
- 当任意 orderer 节点的 kafka 消费者接收到任意节点发出的 TTC-X 消息时,都会将之前收到的交易打包结块,保存在 orderer 本地,之后再分发到各 peer 节点。
个人理解:OSN内部有每个通道对应topic的kafka生产者、kafka消费者,OSN里的生产者同时发送交易到集群,OSN同时消费来自集群的交易,共识的交易达到交易量或达到时间间隔后,OSN的kafka生产者会发送TTC-X到集群,OSN的kafka消费者收到后会将之前收到的交易结块,保存在OSN本地,然后分发到peers节点
以上过程可由下图描述:
Kafka共识集群结构
Kafka简介
注,Orderers(OSN)与Consenter(一致性)不一样,Consenter在kafka集群里,是真正实现共识的共识插件,而OSN是与集群cluster通信获得共识结果的节点
Kafka是一种分布式的,基于发布/订阅的消息系统。主要设计目标如下:
- 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间的访问性能
- 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输
- 支持Kafka Server间的消息分区,及分布式消费,同时保证每个partition内的消息顺序传输
- 同时支持离线数据处理和实时数据处理
Kafka架构
一个典型的kafka集群中包含若干producer,若干broker,若干consumer group,以及一个Zookeeper集群。
- Kafka通过Zookeeper管理集群配置,选举leader,以及在consumer group发生变化时进行rebalance
- producer使用push模式将消息发布到broker
- consumer使用pull模式从broker订阅并消费消息。
各个角色的功能分别是:
-
Brokers(经纪人)
代理是负责维护发布数据的简单系统。- 每个代理可以每个主题具有零个或多个分区。
- push模式的目标是尽可能以最快速度传递消息,但是这样很容易造成consumer来不及处理消息,而pull模式下Broker则可以根据consumer的消费能力以适当的速率消费消息。
- 假设,如果在一个主题和N个代理中有N个分区,每个代理将有一个分区。
-
Producers(生产者)
生产者是发送给一个或多个Kafka主题的消息的发布者。- 生产者向Kafka经纪人发送数据。
- 每当生产者将消息发布给代理时,代理只需将消息附加到最后一个段文件。实际上,该消息将被附加到分区。
- 生产者还可以向他们选择的分区发送消息。
-
Consumers(消费者)
Consumers从经纪人处读取数据。 消费者订阅一个或多个主题,并通过从代理中提取数据来使用已发布的消息。- Consumer自己维护消费到哪个offet,线性增加
- 每个Consumer都有对应的group
- group内是queue消费模型:各个Consumer消费不同的partition,因此一个消息在group内只消费一次
- group间是publish-subscribe消费模型:各个group各自独立消费,互不影响,因此一个消息被每个group消费一次
-
Topics(主题)
属于特定类别的消息流称为主题。- 数据存储在主题中。Topic相当于Queue。
- 主题被拆分成分区。
- 每个这样的分区包含不可变有序序列的消息。
- 分区被实现为具有相等大小的一组分段文件。
-
Partition(分区)
一个Topic可以分成多个Partition,这是为了平行化处理。- 每个Partition内部消息有序,其中每个消息都有一个offset序号
- 一个Partition只对应一个Broker,一个Broker可以管理多个Partition
- 因为每条消息都被append到该partition中,是顺序写磁盘。因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)
-
Replicas of partition(分区备份)
- 副本只是一个分区的备份。 副本从不读取或写入数据。 它们用于防止数据丢失。
- 对于传统的message queue而言,一般会删除已经被消费的消息,而Kafka集群会保留所有的消息,无论其被消费与否。
- 当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此Kafka提供两种策略去删除旧数据。一是基于时间,二是基于partition文件大小。
Ledger账本结构
Peer节点负责维护区块链的账本(ledger)和状态(State),本地的账本称为PeerLedger。
整个区块结构分为文件系统存储的Block结构和数据库维护的State状态,其中state的存储结构是可以替换的,可选的实现包括各种KV数据库(LEVELDB,CouchDB等)
账本简单的说,是一系列有序的、不可篡改的状态转移记录日志
- 状态转移是链码(chaincode)执行(交易)的结果,每个交易都是通过增删改操作提交一系列键值对到账本。一系列有序的交易被打包成块,这样就将账本串联成了区块链。
- 同时,一个状态数据库维护账本当前的状态,因此也被叫做世界状态。
在 1.0 版本的 Fabric 中,每个通道都有其账本,每个 peer 节点都保存着其加入的通道的账本,包含着交易日志(账本数据库)、状态数据库以及历史数据库。
账本状态数据库实际上存储的是所有曾经在交易中出现的键值对的最新值
- 调用链码执行交易可以改变状态数据,为了高效的执行链码调用,所有数据的最新值都被存放在状态数据库中。
- 就逻辑上来说,状态数据库仅仅是有序交易日志的快照,因此在任何时候都可以根据交易日志重新生成。
- 状态数据库会在peer节点启动的时候自动恢复或重构,未完备前,该节点不会接受新的交易。
- 状态数据库可以使用 LevelDB 或者 CouchDB。
- 跟 LevelDB 一样,CouchDB 也能够存储任意的二进制数据,
- CouchDB 额外的支撑 JSON 富文本查询,如果链码的键值对存储的是 JSON,那么可以很好的利用 CouchDB 的富文本查询功能。
Fabric 的账本结构中还有一个可选的历史状态数据库,用于查询某个 key 的历史修改记录,需要注意的是,历史数据库并不存储 key 具体的值,而只记录在某个区块的某个交易里,某 key 变动了一次。后续需要查询的时候,根据变动历史去查询实际变动的值。
账本数据库是基于文件系统,将区块存储于文件块中,然后在 LevelDB 中存储区块交易对应的文件块及其偏移,也就是将 LevelDB 作为账本数据库的索引。
现阶段支持的索引有:
- 区块编号
- 区块哈希
- 交易 ID 索引交易
- 区块交易编号
- 交易 ID 索引区块
- 交易 ID 索引交易验证码
Chaincode链码系统
前面我们提到,状态转移是链码(chaincode)执行(交易)的结果。实际上,链码(chaincode)是 Hyperledger Fabric 提供的智能合约,是上层应用与底层区块链平台交互的媒介。即,合约与交易都与链码相关。
CSCC配置系统
介绍一个新的系统chaincode,叫做配置系统chaincode(CSCC),主要负责处理所有的配置相关的事务。CSCC提供方法查询众多的配置数据,包括通道配置。
链码的通道配置
引导
共识服务由1个或多个Orderers组成。 每个Orderer配置有匹配的创世区块,其由引导CLI命令生成,其提供了一些必要的数据,包括一系列可信根节点的列表,Order证书和IP地址的列表,一组特定的共识算法配置以及访问控制策略(谁可以创建信道)。
要启动并连接到共识服务,peer至少需要以下配置:
- 准入网络的注册证书。 证书可以来自任意CA,只要CA是peer将连接到的共识服务的可信任根的一部分
- 来自共识服务管理CLI生成的Orderer证书和IP地址的列表
- 可信任根节点列表
- peer可以订阅的通道可选列表。 除非明确配置,否则peer在启动时不订阅任何通道
注意,#2和#3来自引导的创世区块,我们可以从引导CLI命令获得。
通过CLI或使用SDK API的应用程序,peer可以订阅已经存在的通道。 orderer通过在通道创建或重新配置期间收到的消息决定谁可以加入通道。
例如,假设peer A和B属于2个不同成员Alice和Bob。 请注意,Alice和Bob可能在网络上有多个Peer,并且他们的任何Peer都可以加入通道。 以下是一个典型的序列:
创建通道
- 应用程序/ SDK获得A和B的背书用于创建通道“foo”的配置交易。
- 应用程序/ SDK调用Broadcast RPC,将背书过的配置交易传递给order服务。
- 应用程序/ SDK然后调用在通道foo上deliver RPC。此RPC将返回一个错误,直到order服务成功创建通道。
- 当通道最终被创建后,Deliver RPC将返回通道的信息到应用程序/ SDK。在这一时点,通道foo应当仅具有包含相关订阅者的创世区块,并且与该配置交易一起被(或最近的重新配置交易)引导。
- 应用程序/ SDK在A和B上调用JoinChannel API,将通道foo的创世区块传递给A和B,添加CSCC到通道上。
- A和B上的CSCC检查创世区块,包括检查区块中的配置交易的背书。如果一切正确,他们调用在通道上的Deliver RPC来开始接收块。
如果通道已经存在,则参与者列表将被替换。Orderers自动替换订阅者并且将该交易与该通道上的其他交易一起发送给新成员,新成员将会同步完整的块。
关闭通道
应用程序可以通过发送类似于创建通道的配置交易来关闭其创建的通道。 它需要根据应用程序设置的策略从通道参与方得到背书。
peer不会自动销毁相关的账本,但是裁剪进程会在适当的时候处理。
应用程序可以继续从已关闭的账本中读取数据,只要该账本尚未被删除,但由于通道已被销毁,因此不能执行交易了。
查询通道
通道只能被该通道的成员查询。也就是说,交易发起方的签名能够被存储在账本配置区块中的CA证书验证通过。这是通过发起一个查询交易到CSCC,同时附上链的ID,返回的结果是一个配置区块,里面包含了成员证书和一些其他的配置信息。
链上的交易
一个交易必须包含目标的链ID(链ID =通道ID =账本ID)。
共识服务将把交易放置在由链ID标识的指定通道上,并且在该通道内被排序,而与其它通道上的交易无关。 最终在该通道上产生一个包含交易的区块并发送到订阅了该通道的那些peer。
注意,每个链都是独立和并行的,因此一个peer可以同时接收和处理不同链上的区块。
chaincode事务只能操作指定链中的状态变量
chaincode限制
- 从交易调用chaincode总是在交易被发送的链上进行操作
- 只有系统链上的chaincode可以被私有链上的其他chaincode调用并且是只读的
API 配置交易(通道与节点绑定)
- 在peer上增加一个新的gRPC API和一个新的顶层交易类型。API允许App / SDK通知peer已成功加入的通道。
加入通道API的输入是由新创建的通道上的共识服务返回的创世区块,peer使用此区块设置与通道关联的账本。 - 新的交易类型称为配置交易,这种类型的交易可以由Orderer和peer处理。 创建或重新配置通道的交易都属于配置交易,其中背书请求是让peer批准和不批准它们创建或重新配置通道。 peer可以通过提案请求返回接受或拒绝。 为了保持灵活性,我们将提供一个系统chaincode来处理通道创建的背书请求,它将自动响应签名请求。
- chaincode还提供查询此通道上参与成员列表的功能。
- 新配置交易必须包含所有先前的配置条目,并且所有新/修改的配置条目必须用其包含配置包络的序列号和链ID标记。每个配置条目都具有枚举类型,唯一(按类型划分)ID以及由名称引用的修改策略。Order服务将根据现有配置策略验证配置交易,如果不满足全部修改策略,则拒绝它。
- SDK可以向API提供进一步的抽象。 例如,它可以提供1个API,创建通道(成员证书列表),它将执行在创建通道部分中讨论的所有6个步骤。
- 最后,SDK将调用应用程序上的回调,返回创建通道的状态。
链码的合约作用
链码(chaincode)是 Hyperledger Fabric 提供的智能合约,是上层应用与底层区块链平台交互的媒介。现阶段,Fabric 提供 Go、Java 等语言编写的链码
所有的链码都实现两个接口,init 和 invoke。
- init 接口用于初始化合约,在整个链码的生命周期里,该接口仅仅执行一次。
- invoke 接口是编写业务逻辑的唯一入口,虽然只有一个入口,但是可以根据参数传递的不同自由区分不同业务逻辑,灵活性很高。比如应用开发者规定 Invoke 接口的第一个参数是合约方法名,剩余的 Invoke 参数列表是传递给该方法的参数,那么就可以在 Invoke 接口方法体中根据方法名的不同分流不同业务了。
合约里能够获取的内容
- 输入参数获取。这点很好理解,我们只有知道此次调用的输入,才能处理逻辑,推导输出;
- 与状态数据库和历史数据库交互。在合约层,我们可以将区块链底层当做是一个键值对数据库,合约就是对数据库中键值的增删改查;
- 与其他合约的交互。在合约执行的过程中,可以与其他合约交换数据,做到类似跨链的效果。有了这种形式的数据获取方式,其实就可以将联系不紧密的业务逻辑拆分为多个合约,只在必要的时候跨合约调用,非常类似于现在提倡的微服务架构。
编写链码还有一个非常重要的原则:不要出现任何本地化和随机逻辑。此处的本地化,不是指语言本地化,而是执行环境本地化。区块链因为是去中心架构,业务逻辑不是只在某一个节点执行,而是在所有的共识节点都执行,如果链码输出与本地化数据相关,那么可能会导致结果差异,从而不能达成共识。
链码部署
Peers是独立实体,通道就是业务载体,链码就是业务;不同的通道即便是运行相同的链码,因为载体不同,可认为是两个不同业务。
- 创建业务载体通道;
- 将通道与 peer 节点绑定;
- 在通道上实例化链码。
通道的管理
通道只有创建,而没有删除功能。但是在使用 kafka 共识的过程中,如果数据操作不当,直接在 kafka 中删除数据,而 orderer 没有逻辑去处理这种异常删除,因此会不断的重试,在达到重试极限后直接崩溃整个进程
没有完善的数据管理方案
在我们的使用场景中,数据增长是很快的,如果使用 CouchDB 作为底层数据引擎,数据更是几何倍数的爆发。现有的解决方案只能是在云上部署节点,提供可持续扩充的云硬盘,再者使用 LevelDB 替换掉 CouchDB,避免使用模糊查询。
Event事件流
事件框架支持发出2种类型的event(事件)
- Block event
- 自定义/chaincode event(在events.proto中定义的ChaincodeEvent类型)
基本思想
client(event consumers\事件消费者)将注册event类型(block或chaincode)。在chaincode的情况下,它们可以指定附加的注册标准,即chaincodeID和eventname。
- ChaincodeID标识client想要查看event的特定Chaincode。
- eventname是Chaincode开发人员,在调用Chaincode中的SetEvent API时嵌入的字符串。
调用transaction是当前唯一可以发出event的操作,并且每个调用,在每个transaction中只能发出一个event。
一般Event类型与ChaincodeEvent的关系
Event与event类型相关联。 客户注册他们想要接收event的event类型。
event类型的生命周期由“block”event来说明
- 在启动peer时,在支持的event类型中添加“block”
- client可以与peer(或多个peers)一起注册感兴趣的“block” event类型
- 创建Block的Peers,向所有注册client发布event
- 客户收到“block” event并处理Block中的事务
Chaincode event添加了额外的注册过滤级别。 Chaincode event不是注册给定event类型的所有event,而是允许client从特定Chaincode注册特定event。 对于目前的第一个版本,为了简单起见,没有在eventname上实现通配符或正则表达式匹配,但后续会提供该功能
Fabric通信方式
节点通信、client与节点Api通信:使用http/2下的gRPC
- http: 基于TCP/IP协议,需要三次握手
- rpc:远程进程调用,需要统一的序列化,不适用于频繁连接
- gRPC: 使用HTTP/2协议并用ProtoBuf作为序列化工具
与REST比较
- 和REST一样遵循HTTP协议(明确的说是HTTP/2),但是gRPC提供了全双工流
- 和传统的REST不同的是gRPC使用了静态路径,从而提高性能
- 用一些格式化的错误码代替了HTTP的状态码更好的标示错误
gRPC
- xxx.proto, 定义rpc,输入参数与返回参数的数据命名结构
- 命令行中protoc编译生成对应的xxx.pb.go源码,编写clientAPI for EventService供客户端使用的接口定义、接口实例、接口实例的初始化函数,和server API for EventService供服务端使用的接口定义,注册函数。
HTTP/2特点
-
将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码。在HTTP/2中,数据流以消息的形式发送,而消息由一个或多个帧组成,帧可以在数据流上乱序发送,然后再根据每个帧首部的流标识符重新组装。二进制分帧是HTTP/2的基石,其他优化都是在这一基础上来实现的。我们先了解几个概念:
- 帧(Frame):HTTP/2通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流。
- 消息(Message):由一个或多个帧组合而成,例如请求和响应。
- 连接(Connection):与 HTTP/1 相同,都是指对应的 TCP 连接;
- 流(Stream):已建立的连接上的双向字节流。
-
支持请求与响应的多路复用来减少延迟
- 同域名下所有通信都在单个连接上完成。
- 单个连接可以承载任意数量的双向数据流。
- 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。
这一特性,性能会有极大的提升,因为:
- 同个域名只需要占用一个TCP连接,消除了因多个TCP连接而带来的延时和内存消耗。
- 单个连接上可以并行交错的请求和响应,之间互不干扰。
-
压缩HTTP首部字段将协议开销降至最低
- HTTP/2在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;
- 首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
- 每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。
对请求划分优先级
支持服务端Push消息到客户端
ProtoBuf:
- 一套用于数据存储,网络通信时用于协议编解码的工具库.它和XML和Json数据差不多,把数据已某种形式保存起来.Protobuf相对与XML和Json的不同之处,它是一种二进制的数据格式,具有更高的传输,打包和解包效率.
- 如果使用protobuf实现,首先要写一个proto文件(不妨叫Order.proto),在该文件中添加一个名为"Order"的message结构,用来描述通讯协议中的结构化数据。使用protobuf内置的编译器编译 该proto。
参考资料:
Hyperledger文档:(详细研读)
https://hyperledger-fabric.readthedocs.io/en/latest/whatis.html
Hyperledger github文档
https://github.com/hyperledger/fabric/tree/release-1.1/docs/source
常用的Q&A
https://hyperledger-fabric.readthedocs.io/en/latest/Fabric-FAQ.html?highlight=consenter
知乎专栏翻译:
https://zhuanlan.zhihu.com/p/23356616
专栏-fabric生命周期
https://zhuanlan.zhihu.com/p/25119939
hyperledger-MSP event等侧面分析 图解
https://blog.csdn.net/maixia24/article/category/7507736