Gossip在Hyperledger Fabric中发挥着重要的作用。在这个教程中,我们将分阶段考察Fabric网络启动时gossip的运行机制,学习Fabric中的一些核心概念,例如主导节点/leader、锚节点/anchor等,理解
gossip是如何帮助Hyperledger Fabric成为一个可伸缩的联盟链平台。
Hyperledger Fabric区块链开发教程与工具:
- Fabric区块链Node.js开发详解
- Fabric区块链Java开发详解
- Fabric区块链Golang开发详解
- BYFN一键启动工具Windows版
- Fabric快速开发工具箱
- Fabric链码Python开发包
我们大多数都是从Hyperledger Fabric自带的演示网络例如First Network开始学习并尝试Fabric区块链的。First Network提供了一个脚本byfn.sh向我们展示了启动一个Fabric网络的典型流程:
经过以上操作,Fabric网络就准备好了,接下来通常就是部署包含业务逻辑的链码。
在上述流程中有些藏在后台的过程很有意思。在这个教程中我们主要考察gossip的作用,了解它是如何帮助实现一个可伸缩的解决方案的。Hyperledger Fabric采用gossip作为点对点通信机制。为了优化整个信息流转过程,Fabric实现了gossip数据扩散协议以达成可伸缩的网络部署和运营。
Hyperledger Fabric官方文档中的gossip提供了一个总体上的描述。在这里我们将抽取相关的信息并通过演示来看看gossip在实际运作中的表现。
Gossip引导节点配置需要在每个对等节点/peer上进行,该配置包含了同一机构中的一个或一组对等节点。当一个peer启动运行后,它就会联络列表中的其他peer节点进行消息的gossip传播。
在经典的First Network配置中,在每个机构中有两个peer节点,因此一个合理的设置就是将另一个peer节点作为gossip的引导节点,这也是我们在配置文件(base/docker-compose-base.yaml)中观察到的。下面只显示了Org1的peer节点的信息,不过我们可以在Org2的peer节点配置中观察到类似的信息:
当peer节点启动后,它会先查找用于gossip通信的引导节点。后续我们也会看一下如果没有定义引导节点会发生什么情况。
一个机构中的主导节点/leader负责从排序服务接收区块并分发给同一机构中的其他peer节点。请记住在一个fabric网络中,所有的peer节点都需要从排序服务接收新区块。leader节点的引入优化了新区块同步到所有peer节点的流程,因为排序服务只需要向每个机构的leader节点发送新区块就可以了,接下来由leader节点负责同步到其他peer节点。
每个机构都有自己的leader节点。在演示中我们将看到这一点。每个通道也都有自己的leader节点。在peer节点加入一个通道之前,它是没有leader这个概念的,只有当它加入通道之后,才涉及leader节点的问题。
在一个机构中,leader节点可以静态配置或者动态选举。在First Network中的默认设置是通过选举决定,该配置在base/peer-base.yaml中:
在一个网络中需要锚节点/anchor peer来获取其他机构的通道成员信息。如果没有锚节点的化,那么对通道成员的了解将局限在当前机构内。例如,当没有锚节点时,在Org1中的peer节点只了解Org1内部的其他peer节点是通道成员,但是没法了解Org2机构内的peer节点也是通道成员。在一个通道中至少需要一个锚节点以打破这种信息的割裂。
锚节点的配置需要对通道进行更新操作。首先使用configtxgen创建一个更新交易,签名该交易并发送给排序服务,排序服务就会生成一个新的配置区块,其中包含了这个用于更新锚节点配置的交易。当这个区块到达通道中所有的peer节点并被各节点提交后,所有的peer节点也就都了解哪个是锚节点了,并进而通过彼此的gossip传播实现了对整个通道成员的信息了解,即包括机构内成员,也包括机构外成员。
在后面部分我们将看到配置锚节点前后的不同运行情况。
我们使用的是First Ntwork,其中包含两个机构Org1和Org2,每个机构包含两个peer节点(peer0和peer1),定义了通道mychannel并将所有4个peer节点加入该通道,两个机构中的peer0均被配置为锚节点。
当执行byfn.sh时,该脚本执行以下任务:
网络已经就绪,每个peer节点都了解通道中的其他节点。下面是执行脚本后的结果(使用-n选项是告诉byfn脚本不要部署链码):
在下面的演示中我们基本遵循上述流程。为了更好地观察实验结果,我们将逐个分解peer节点的docker-compose配置,之后也会修改配置文件或者重新安排先后顺序并观察不同的结果。
从first-network/直接拷贝一个新目录:
cd fabric-samples
cp -r first-network kc-first-network
我们将在这个目录上进行后续操作:kc-first-network/
同时,原始的CLI容器依赖于所有启动的网络组件。由于我们将逐个启动网络组件,因此先注释掉依赖关系。
我们利用byfn.sh来生成Fabric网络所需的密码学资料和通道配置数据。
cd kc-first-network
./byfn.sh generate
docker-compose -f docker-compose-cli.yaml up -d orderer.example.com cli
docker-compose -f docker-compose-cli.yaml up -d peer0.org1.example.com
打开终端查看peer0.org1.example.com的日志:
docker logs -f peer0.org1.example.com
让我们重点关注gossip相关的活动。从日志中我们可以了解gossip的初始化过程以及gossip实例的启动过程。
然而,由于配置了gossip的引导节点,peer0.org1.example.com 会联络peer1.org1.example.com,
但是peer1目前还没有运行。
docker-compose -f docker-compose-cli.yaml up -d peer1.org1.example.com
我们没有看到和peer0类似的无法访问gossip引导节点的问题。实际上peer1.org1.example.com
也配置了peer0.org1.example.com作为其gossip引导节点,并且在启动时会尝试联络peer0.org1.example.com。由于peer0已经启动了,因此从日志中可以看到peer1成功联络到peer0(grpc.method=Ping, grpc.code=OK)。
几乎在peer1.org1.example.com启动的同时,peer0.org1.example.com也会联络peer1.org1.example.com。可以看到时间戳是吻合的。
这表示两个peer节点在gossip层已经连接上了。注意目前还没有通道相关
的角色例如leader节点或anchor节点,只有加入通道后才会涉及这些概念。
为了遵循byfn.sh脚本的演示流程,我们也启动peer0.org2.example.com 和 peer1.org2.example.com 。
可以观察到类似的行为:
docker-compose -f docker-compose-cli.yaml up -d peer0.org2.example.com peer1.org2.example.com
由于两个节点几乎同时启动,因此我们没有看到之前在step3出现的peer0连接gossip引导节点的问题。
docker exec cli peer channel create -o orderer.example.com:7050 --tls \
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
-c mychannel -f ./channel-artifacts/channel.tx
结果genesis区块文件mychannel.block保存在CLI容器内,该文件用于将peer节点加入通道mychannel。
注意CLI容器的默认设置是以Org1的Admin身份连接peer0.org1.example.com。
docker exec cli peer channel join -b mychannel.block
从日志中可以观察到以下内容:
docker exec \
-e CORE_PEER_ADDRESS=peer1.org1.example.com:8051 \
-e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt \
cli peer channel join -b mychannel.block
可以观察到和peer0.org1.example.com的日志类似的内容,除了主导节点的选举部分。
因此,从通道角度看,peer0.org1.example.com是Org1的主导节点,负责从排序服务接收新区块并发送给Org1中的其他peer节点。
当一个机构内peer节点的主导关系确定后,我们立即可以从两个节点的日志中看到它们已经了解了通道中的成员信息。
peer0.org1.example.com: now know peer1.org1.example.com as channel member
peer1.org1.example.com: now know peer0.org1.example.com as channel member
docker exec \
-e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp \
-e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 \
-e CORE_PEER_LOCALMSPID="Org2MSP" \
-e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \
cli peer channel join -b mychannel.block
docker exec \
-e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp \
-e CORE_PEER_ADDRESS=peer1.org2.example.com:10051 \
-e CORE_PEER_LOCALMSPID="Org2MSP" \
-e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt \
cli peer channel join -b mychannel.block
和之前Org1的情况类似,我们可以看到:
现在我们知道gossip已经在单一机构内工作正,因为peer节点彼此都
了解对方的存在了。不过这还没结束,因为peer节点还需要了解其他
机构中的peer节点,这需要配置锚节点/anchor peer。
docker exec cli peer channel update -o orderer.example.com:7050 --tls \
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
-c mychannel -f ./channel-artifacts/Org1MSPanchors.tx
我们首先看到生成了一个新的区块block#1并且被Org1和Org2的所有peer节点接收并提交。让我们研究一下。
首先,我们看一下新的区块是如何到达各peer节点的。我们知道新区块是orderer生成的。从tcpdump的trace中我们只看到orderer.example.com在与peer0.org1.example.com和peer0.org2.example.com通信,而没有与两个机构中的peer1节点通信。这是因为两个机构中的peer0是主导节点,orderer只需要把新区块发给主导节点。
注意这里只显示了两个包,实际上有一系列的包从orderer发送给两个机构的peer0节点。不给过我们没有看到发给peer1的包。
另外,通过捕捉每个peer节点上的接收block#1的日志,我们可以断定:
我们看到两个机构中的peer1接收到block#1都要比peer0晚一点。虽然这不是充分的中举,至少它符合我们所说的由主导节点负责转发新区块的理论。这就是伸缩性实现的机制,通过引入主导节点并将新区块的广播拆分为两层。
接下来我们将考察每个peer节点将区块提交给账本之后会发生什么情况。一旦了解到锚节点的存在,这些peer节点都会执行gossip操作。经过几轮gossip扩散,我们可以看到通道的成员信息:
现在每个peer节点都已经掌握了完整的成员映射表。作为额外的福利,我们也看到Org2的peer节点是如何在后续的gossip过程中了解到peer1.org1.example.com这个节点的。
现在通道中的每个peer节点都已经知晓彼此的存在,无论在同一机构还是不同机构,网络已经就绪了。
我们看到在一个通道中只需要一个锚节点就可以让网络正常工作。不过还是推荐每个机构都设置一个锚节点。
docker exec \
-e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp \
-e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 \
-e CORE_PEER_LOCALMSPID="Org2MSP" \
-e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \
cli peer channel update -o orderer.example.com:7050 --tls \
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
-c mychannel -f ./channel-artifacts/Org2MSPanchors.tx
我们现在可以看到通道成员视图的变化。
Gossip的引导节点配置在 base/docker-compose-base.yaml中。注释掉每个peer节点的CORE_PEER_GOSSIP_BOOTSTRAP 变量。从日志中的主要发现如下:
在step 3和4之后,我们可以看到Org1的两个peer节点在gossip层面不再通信。在Step 5之后,Org2的两个节点也存在类似的情况。
当所有peer节点都加入mychannel通道后,我们观察到所有的peer节点都被选举为主导节点。
这是由于在机构内部缺乏gossip沟通。不过这有什么问题吗?让我们看看在更新锚节点(Step 11)之后会怎么样。
经过gossip过程,通道中所有的peer节点都掌握了正确的通道成员信息。同时我们还观察到有些peer节点已经不再做主导节点了。
我们的观察表明,在没有配置peer节点的gossip引导节点时,一个机构内的主导节点选举是失控的,因为每个peer都会选自己做主导节点。无论如何这不是期望的情况,因为orderer需要和每个主导节点通信。当更新锚节点配置后,所有的peer节点最终都掌握了通道成员信息,同时有节点也不再是主导节点,这是我们所期望的。因此最终的结果是可行的,即使我们在开始时没有设置每个peer节点的gossip引导节点。
原文链接:Fabric Gossip实验教程 — 汇智网