跳转至

区块链网络中的生活

现在,您的 Fabric 网络应该已经设置好,并且正在运行您的应用程序,通过智能合约连接不同的实体,并通过 web 界面为用户提供服务。此外,为了帮助您的开发人员和系统管理员维护代码、推送更新和管理网络配置,您应该制定一个流程,通过该流程可以在适当的保护措施下完成系统测试和维护,并且不会中断服务。

然而,这不会是您的应用程序的最终状态。需求和期望是不断发展的,对于涉及多个协作实体的应用程序来说尤其如此,这些实体在不同的时间点会有不同的需求。此外,即使应用程序的性质和功能保持不变,软件本身也会不断变化和发展。最后,任何分布式面向服务的应用程序(可以应用于任何 Hyperledger Fabric 应用程序的描述)都必须为最终用户的性质和数量随时间的增加或减少做好准备,这就需要改变硬件和软件资源分配。

因此,在区块链应用程序的整个生命周期中,您会看到许多需要更新代码和配置的更改。前面列出的这些变化并不是光纤网络或区块链独有的,但我们需要使用的机制以及选择这些机制时的考虑因素都是特定于平台的。这些将是本章的主要焦点,虽然不是唯一的焦点。我们将首先检查您的 Fabric 应用程序可能需要修改的不同方式,并通过示例代码和配置以及规划系统升级的指南来说明具体的场景。然后,我们将讨论应用和网络成员的变化,以及适用于行业规模的区块链应用的相关考虑因素。在本章的后端,我们将深入探讨系统维护:监控应用程序和系统资源的健康状况,设计或升级系统以确保高性能。

本章将涵盖以下主题:

  • 修改或升级 Hyperledger 结构应用程序
  • 织物区块链和应用生命周期
  • 将新组织添加到网络中
  • 修改链码逻辑
  • 链代码中的依赖性升级
  • 背书政策更新
  • 系统监控和性能
  • 分析容器和应用程序
  • 测量应用程序性能

修改或升级 Hyperledger 结构应用程序

第 5 章展示网络资产和交易中介绍的通用 Hyperledger Fabric 应用程序的设计,提供了在其生命周期内可能需要的升级类型的提示。让我们来看看光纤网络及其用户的需求随时间变化的各种方式:

  • 软件更新:变更和升级是软件维护不可或缺的一部分。更常见的情况是,需要修改来修复错误、性能低下和安全缺陷(例如,考虑 Windows Update 服务)。虽然几乎同样不可避免,但较不经常的是,必须对软件进行重大的设计变更,以应对无法预料的挑战。此外,鉴于大多数应用程序依赖于其他(第三方)软件,后者的任何升级都会触发前者的相应变化。可以将 Windows 服务包看作一个类比。 在 Hyperledger Fabric 世界中,作为应用程序开发人员或系统管理员,您必须支持应用程序级升级和平台级升级。前者涉及错误修复和应用程序逻辑和错误修复的更改,后者涉及底层结构软件的更改。软件更新过程是众所周知的,一些技术在 第 5 章揭露网络资产和交易中讨论;用于测试和可靠的故障转移,也适用于错误修复和一般维护。

如果您还记得我们的规范 Fabric 应用程序的 3 层架构,那么由中间件(使用 Fabric SDK)、web 服务器和用户界面组成的上层通常都在一个组织的控制之下,因此它们可以通过该组织内部制定的流程进行更新。但是,正如我们在 第八章区块链网络中的敏捷性中所看到的,智能合同或链码是一个特例,因为它是一个由所有参与组织集体商定和开发的软件。因此,对 chaincode 的任何更新也必须是共识驱动的,它不像在测试后强制更新那样简单。我们将在本节后面的例子中描述链码升级过程。 最后,结构软件的升级可能会影响功能和数据,因此必须小心谨慎。我们将在本节的后面描述这些机制和缺陷。

  • 不断变化的资源需求:在应用程序生命周期的开始,您为运行应用程序而分配的资源,就像应用程序代码一样,不太可能满足不断变化的用户需求。随着时间的推移,您的应用程序很可能会收到越来越多的用户流量,任何软件改进都无法弥补硬件的限制。类似地,如果我们回想一下 RAS 的需求(参见第 5 章公开网络资产和事务),分布式应用程序的正常运行需要跨系统资源的冗余、故障转移和负载平衡。 就结构而言,这意味着您可能需要向网络中添加更多节点。您可能需要更多的对等点来处理交易认可请求,并且网络作为一个整体可能需要更多的订购者节点来处理和平衡当前瓶颈订购服务的负载(另一方面,如果流量太低,可以删除节点以节省成本)。否则,您可能需要组织中额外的对等节点来进行确认,或者额外的订购者节点来实现更可靠的分布式共识(尽管这可能会带来性能成本)。不管网络中添加和删除节点的原因是什么,作为 Fabric 开发人员或管理员,您都必须支持这种性质的升级,我们将在本部分的后面看到如何实现这一点。 ** 改变用户成员资格:除了用户流量的变化,还必须为用户成员资格随时间的变化做好准备。在结构术语中,这意味着添加或删除被允许向应用程序发送请求和查看应用程序状态的用户或客户端。在一个组织中,始终需要添加或移除有权访问区块链的用户,以及提升或降低授予现有用户的权限。我们已经在 第 5 章展示网络资产和交易中讨论了成员资格创建和授权的示例,在本节的稍后部分,我们将了解如何使用运行时配置更新通道策略。* 更改应用程序策略:Hyperledger Fabric 应用程序中的交易(链码调用)必须满足由参与者集体决定的认可策略。这是可能的,甚至是可以预期的,因为各种不同的原因,这些政策会随着时间而改变,包括性能(我们将在本章的后半部分讨论)。)例如,用于批准每个组织的成员的签署策略可以放宽到只需要两个组织签署的要求。另一方面,政策可以变得更加严格,以克服区块链参与者之间缺乏信任的问题。Fabric 提供的修改背书策略的机制将在本节后面的示例中讨论。* 改变网络配置*:最后,我们始终需要修改区块链网络本身,以满足更高的期望。随着时间的推移,可能会有更多的组织希望参与到应用程序中来,尤其是当应用程序的初始版本证明了它们的价值时。出于几个原因,一些组织可能也想离开。即使在给定的组织内,也可能需要扩展或重新平衡专用于相关应用程序的资源。现在,即使大多数分布式应用程序面临这些需要增强和资源重新配置的情况,区块链应用程序由于其独特的性质也有特殊的需求。回想一下,区块链是一个共享的分类帐,必须由每个参与的网络对等体使用共同的、一致同意的规则来验证和接受。因此,网络本身的结构和属性必须共同商定并记录在分类账中。在 Hyperledger Fabric 术语中,应用程序建立在一个或多个通道(区块链实例)上,其规则和内容对应用程序参与者是私有的。因此,网络中的任何变化都需要将配置变化应用于信道。添加具有其自己的对等组的新组织或删除组织将需要信道重新配置,对等体或订购者地址的改变以及组织内锚定对等体的选择也是如此。其他示例包括通道的核心属性,如块大小和超时;用于读取、写入和管理操作的通道访问策略;哈希机制;和用于订购服务的一致模式。虽然全面介绍通道配置用例超出了本章的范围,但我们将在本节后面的示例中了解如何在结构网络中推动重新配置。

*总之,对结构应用程序的更改不仅需要代码和配置更改、测试和更新的常规软件维护程序,还需要区块链特有的共识驱动操作。在本节的剩余部分,我们将重点介绍 Hyperledger Fabric 支持的两种主要应用程序更新模式。

  • 信道配置更新:这包括组织的添加和移除、资源改变(对等节点和订购节点的添加、移除或修改)、信道属性的改变(策略和块创建规则、散列和共识机制)。
  • 智能合约更新:包括链码和交易背书政策的变更。

稍后,我们将简要介绍结构平台软件的升级。

为了实现这样的升级,我们需要用合适的机制来扩充我们从第 3 章到第 7 章创建的应用程序和工具集。幸运的是,Fabric 平台的设计者预见到了我们在本章中讨论的各种演变,我们用来构建我们的贸易应用程序的初始版本的 SDK(参见 第 5 章公开网络资产和交易)提供了构建这些机制所必需的功能。在讨论实现细节之前,让我们回顾一下 Fabric 事务管道,并对其进行修改以包含更新。

织物区块链和应用生命周期

考虑我们作为一个 Fabric 应用程序实现的贸易场景,图 5.3 中说明了各个阶段:区块链应用程序的创建和运行阶段(见第 5 章图 9.1:区块链应用程序生命周期中的各个阶段(为了方便起见,我们在图中省略了分类帐和事件排放,因为它们不需要解释)

图 9.1:区块链应用程序生命周期的各个阶段

此图并不意味着是一个 Fabric 应用程序所有可能阶段的详尽表示,而是最显著的阶段。

正如我们所看到的,有些类型的更新比其他类型的更新需要更多的操作。无论是在现有组织中还是在新增加的组织中,任何认可对等节点的增加都需要将这些对等节点明确加入到通道中,并随后在这些对等节点上安装当前版本的 chaincode。在那些对等体上不需要显式的实例化;网络对等体之间的八卦协议最终会将共享账本的最新副本同步到新添加的账本上。然而,智能合同修改过程将需要在对等体上安装新版本的链码之后进行明确的渠道范围升级。这个升级步骤相当于最初的实例化,尽管它作用于当前状态而不是空白分类帐。在某些情况下,链码和认可政策的升级可能会紧接在添加新组织的渠道重新配置之后;在这种情况下,可以跳过在新对等体上安装当前版本的链码,而直接安装升级后的链码版本。我们将在下一小节中描述如何扩充我们的交易应用程序来实现这样的系统升级。

在我们继续之前,让我们了解当系统经历不同种类的变化时,区块链是什么样子的。图 9.2 显示了为不同应用程序操作添加了不同类型块的区块链部分:

图 9.2:具有配置块、包含部署事务的块和常规链码事务的区块链的一部分

正如我们所看到的,我们的区块链(或者换句话说,共享分类帐事务日志)从一个 genesis 块(通道上的第一个配置块)开始,它包含通道的初始配置。下一步是链码初始版本的部署和实例化,随后是常规操作(链码调用)。在某些时候,可以添加具有对等体的新组织,这导致另一个配置块被添加到链中,覆盖先前的配置块。类似地,可以创建和升级新版本的链码,升级记录在块中。在这些配置和部署块之间,可以发生常规的链代码事务,并且根据配置的块大小,可以将一个或多个事务捆绑在一个块中并附加到链中。现在让我们来看看如何扩展我们的交易应用程序,以实现我们在本章中已经讨论过的特性。

频道配置更新

正如本章前面所提到的,有许多原因可能需要改变通道配置。由于信道行为完全由其配置决定,并且任何更新都记录在区块链上,因此覆盖了先前的配置,这是一个非常敏感的操作,必须限制特权用户,就像我们的应用程序创建步骤的初始部分,如信道创建和加入(参见 第 5 章暴露网络资产和交易)一样。对通道配置更改的详尽讨论和演示超出了本书的范围,但是我们将展示更新的机制以及在我们的应用程序中包装这些机制的方法;这种机制和过程可以应用于任何配置改变。

为了进行演示,我们将使用常见的情况,即必须向应用程序中添加新的组织和对等方。考虑我们的贸易场景,到目前为止,出口商和它的银行共享一个组织,该组织的 MSP 和 peer 由后者维护。进口商和它的银行也属于同一个组织,其逻辑是银行有更多的动机和资源来维持同业和 MSP。但这种逻辑可能不会永远成立。比方说,我们的出口商,开始是一个小规模的经营者,随着时间的推移,获得了更高的利润和更高的诚信和质量声誉。如今,作为一个拥有巨额现金储备和市场影响力的大型原材料出口国,它有动机以同行而非银行依赖者的身份加入区块链的贸易网络。它还在不同的银行拥有银行账户,因此有同时参与多个区块链(渠道)的需求和潜力。它希望继续参与贸易渠道和包装应用程序,但在自己的组织中,运行自己的 MSP 和自己的网络对等体,独立于银行。

我们必须创建的网络如图所示图 9.3:出口商(或出口实体)的组织、MSP 和同行的扩大贸易网络:

图 9.3:出口商(或出口实体)的组织、MSP 和同行的扩大贸易网络

我们将新组织称为ExportingEntityOrg,它的 MSP 称为ExportingEntityOrgMSP,对等导出实体称为。这是因为名称出口商、ExporterOrgExporterOrgMSP已经在我们的网络中被用来代表出口商的银行;新的组织和对等方必须有唯一的名称。

向网络中添加新组织的先决条件

升级网络所需的工具与 第 3 章设置业务场景中使用的工具相似:

  1. 克隆 Fabric 源代码库:

    1. 运行make docker为同行和订购者构建 Docker 映像。
    2. 运行make configtxlator来生成运行本节中描述的网络创建命令所需的工具(当我们将注意力转向中间件代码时,我们将使用configtxlator)
  2. 此外,我们假设读者遵循了在 第 3 章中描述的步骤,以商业场景设置阶段,并且已经为早期的 4 组织网络创建了信道配置和加密材料文件。

如果您还记得,在 第 3 章设置业务场景的阶段中,我们为四个组织创建了通道工件和加密材料,包括起源块、初始通道配置、每个组织的锚节点配置,以及涉及节点、客户端和 MSP 的所有网络操作的证书和签名密钥。这些配置分别在网络文件夹的configtx.yamlcrypto-config.yaml中定义,并使用configtxgencryptogen工具进行处理。显然,必须修改这些配置来添加新的组织,但是更改配置可能会很麻烦。好消息是,我们可以通过创建额外的配置文件并保持原始文件不变来增加我们的网络。这样,管理员就可以很容易地跟踪组织结构和资源的演变。我们的增量配置文件定义在network/add_org/文件夹中。

生成网络加密材料

crypto-config.yaml文件仅包含关于新组织的信息,足以生成证书和签名密钥:

PeerOrgs: 
  # ExportingEntityOrg 
  - Name: ExportingEntityOrg 
    Domain: exportingentityorg.trade.com 
    EnableNodeOUs: true 
    Template: 
      Count: 1 
    Users: 
     Count: 1 

正如我们所看到的,该规范与我们为最初的四个组织定义的规范相同,除了 MSP 名称和组织域反映了导出实体组织的性质。要为该组织生成加密材料,运行cryptogen命令,如 第 5 章公开网络资产和交易,但这次使用在add_orgs文件夹中定义的配置文件:

cryptogen generate --config=./add_org/crypto-config.yaml 

输出被保存到crypto-config/peerOrganizations,除了现有组织的文件夹之外,您还会看到一个名为exportingentityorg.trade.com的文件夹。该文件夹包含我们新组织的密钥和证书。

产生信道伪像

类似地,configtx.yaml在 organizations 部分只包含出口实体组织的规范,如下所示:

Organizations: 
  - &ExportingEntityOrg 
    Name: ExportingEntityOrgMSP 
    ID: ExportingEntityOrgMSP 
    MSPDir: ../crypto-config/peerOrganizations/exportingentityorg.trade.com/msp 
    AnchorPeers: 
      - Host: peer0.exportingentityorg.trade.com 
        Port: 7051

该规范基本上复制了所有其他组织和对等体的规范;只有名称和路径被修改以识别和设置新的组织(假设在当前目录中已经生成了一个crypto-config文件夹)。要构建增量通道配置,请运行以下命令:

FABRIC_CFG_PATH=$PWD/add_org && configtxgen -printOrg ExportingEntityOrgMSP > ./channel-artifacts/exportingEntityOrg.json 

在这里,我们遇到了与第三章中遵循的步骤的第一个不同之处,即使用业务场景 设置阶段;我们没有为配置块、锚节点等构建单独的文件,而是构建了一个包含所有相关信息的 JSON 规范,包括管理用户的策略规范和证书、CA 根和导出实体组织的 TLS 根,并将其保存到channel-artifacts文件夹中。在本节的后面,我们将在通道配置更新过程中使用这个 JSON。

*为了确保configtxgenadd_org目录中寻找configtx.yaml,我们必须临时改变FABRIC_CFG_PATH环境变量。

在一个操作中生成配置和网络组件

您还可以使用 trade.sh 脚本执行所有上述操作。只需在network文件夹中运行以下命令:

./trade.sh createneworg

通道名称被隐含地假定为tradechannel

除了创建加密材料和通道配置之外,该命令还为add_org/docker-compose-exportingEntityOrg.yaml中的新组织生成 docker-compose 配置。它运行以下服务:

  • 导出实体的组织的一个结构对等实例
  • 导出实体组织的结构 CA 的一个实例

规范和依赖关系就像我们在 第 3 章中的docker-compose-e2e.yaml中遇到的那些,用业务场景设置阶段,如下所示:

services: 
  exportingentity-ca: 
    image: hyperledger/fabric-ca:$IMAGE_TAG 
    environment: 
      - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server 
      - FABRIC_CA_SERVER_CA_NAME=ca-exportingentityorg 
      - FABRIC_CA_SERVER_TLS_ENABLED=true 
      - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.exportingentityorg.trade.com-cert.pem 
      - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/fc435ccfdaf5d67251bd850a8620cde6d97a7732f89170167a02970c754e5450_sk 
    ports: 
      - "11054:7054" 
    command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.exportingentityorg.trade.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/fc435ccfdaf5d67251bd850a8620cde6d97a7732f89170167a02970c754e5450_sk -b admin:adminpw -d' 
    volumes: 
      - ../crypto-config/peerOrganizations/exportingentityorg.trade.com/ca/:/etc/hyperledger/fabric-ca-server-config 
    container_name: ca_peerExportingEntityOrg 
    networks: 
      - trade 

  peer0.exportingentityorg.trade.com: 
    container_name: peer0.exportingentityorg.trade.com 
    extends: 
      file: ../base/peer-base.yaml 
      service: peer-base 
    environment: 
      - CORE_PEER_ID=peer0.exportingentityorg.trade.com 
      - CORE_PEER_ADDRESS=peer0.exportingentityorg.trade.com:7051 
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.exportingentityorg.trade.com:7051 
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.exportingentityorg.trade.com:7051 
      - CORE_PEER_LOCALMSPID=ExportingEntityOrgMSP 
    volumes: 
        - /var/run/:/host/var/run/ 
        - ../crypto-config/peerOrganizations/exportingentityorg.trade.com/peers/peer0.exportingentityorg.trade.com/msp:/etc/hyperledger/fabric/msp 
        - ../crypto-config/peerOrganizations/exportingentityorg.trade.com/peers/peer0.exportingentityorg.trade.com/tls:/etc/hyperledger/fabric/tls 
        - peer0.exportingentityorg.trade.com:/var/hyperledger/production 
    ports: 
      - 11051:7051 
      - 11053:7053 
      - 11055:6060 
    networks: 
      - trade 

这个文件是使用模板 YAML add_org/docker-compose-exportingEntityOrg-template.yaml生成的,在FABRIC_CA_SERVER_TLS_KEYFILE和命令中的 CA 密钥文件名(由变量EXPORTINGENTITY_CA_PRIVATE_KEY表示)都被替换为crypto-config/peerOrganizations/exportingentityorg.trade.com/ca/中的秘密密钥文件名,在前面的例子中是fc435ccfdaf5d67251bd850a8620cde6d97a7732f89170167a02970c754e5450_sk

该密钥文件名将随着cryptogen工具的每次执行而变化。

此外,注意环境变量exportingentity-ca:FABRIC_CA_SERVER_TLS_CERTFILE中的证书文件名和 volumes 部分中指定的路径与使用cryptogen生成的相匹配。id、主机名和端口值与在congfigtx.yaml文件中指定的相匹配。最后,我们确保容器端口被映射到唯一的端口(在 11,000 个范围内),以避免与旧组织的对等体和 MSP 的容器所公开的端口冲突。

启动新组织的网络组件

要为我们的新组织启动 peer 和 MSP,只需运行以下命令:

docker-compose -f add_org/docker-compose-exportingEntityOrg.yaml up

如果您愿意,可以将它作为后台进程运行,并将标准输出重定向到日志文件。否则,您将在控制台上看到各种容器的启动和日志显示。在不同的终端窗口中,如果您运行docker ps -a,您将看到以下两个额外的容器:

CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES 
02343f585218    hyperledger/fabric-ca:latest    "sh -c 'fabric-ca-se..."    16 seconds ago    Up 16 seconds    0.0.0.0:11054->7054/tcp    ca_peerExportingEntityOrg 
a439ea7364a8    hyperledger/fabric-peer:latest    "peer node start"    16 seconds ago    Up 16 seconds    0.0.0.0:11055->6060/tcp, 0.0.0.0:11051->7051/tcp, 0.0.0.0:11053->7053/tcp    peer0.exportingentityorg.trade.com 

您可以使用存储库中的脚本文件启动网络,如下所示:

./trade.sh startneworg 

通道名称被隐含地假定为tradechannel

这将在后台启动容器,您可以在logs/network-neworg.log中查看日志。现在我们的网络有 5 个对等点、5 个 MSP 和一个订购者运行在不同的容器中。我们现在准备开始重新配置渠道的过程,以接受新的组织。

要停止与导出实体的组织相关联的容器,只需运行./trade.sh stopneworg

这不会清除所有卷(运行 docker 卷以进行检查),因为初始 4-org 网络的容器仍在运行。只有在您拥有自己的整个网络后,您才能清除剩余的活动卷。)

更新频道配置

现在我们将注意力转向中间件。在 第 5 章暴露网络资产和交易中,当我们创建tradechannel时,区块链是用使用configtxgen工具创建的 genesis 块初始化的。genesis 块恰好是通道的第一个配置块。后续的通道配置更改包括向通道追加新的配置块,每个配置块都有唯一的版本,并且最新的配置块会覆盖先前的配置块。在升级场景中,将被覆盖的是 genesis 块中的配置,因为我们假设自从我们的通道被创建并准备好在 第 5 章公开网络资产和交易中使用以来,没有进行其他更改。

升级通道配置的逻辑位于我们代码库中的middleware文件夹的upgrade-channel.js中,它基于 Fabric SDK 节点 API。还需要以下先决条件:

  • 这是根据本章前面的 Fabric 源代码构建的。请确保它位于您的系统路径中。
  • 这是一个命令行 JSON 处理器,用于创建和解析 JSON 对象。在 Ubuntu 系统上,你可以使用apt-get install jq来安装它。请确保它也位于您的系统路径中。

upgradeChannel函数中,有创建客户端和通道对象的样板代码,读者应该已经熟悉了。通道升级程序需要从每个现有组织(我们网络中有 4 个)的管理用户处收集新配置的签名,就像通道创建程序一样。但是在生成和收集签名之前,还需要许多额外的步骤。首先,我们需要从订购者那里获取最新的配置块。我们在代码中使用以下函数调用来实现这一点:

channel.getChannelConfigFromOrderer(); 

这将返回一个块configuration_block,其配置字段包含当前通道配置。该配置的版本可以从配置的序列字段中提取,如下:configuration_block.config.sequence。完整的配置规范在 Fabric 源代码中定义为 protobuf ( common.Config),对它的检查留给读者作为练习。

在代码中,我们现在创建一个文件夹来存储将在后续步骤中创建的临时文件。这些文件是使用configtxlator工具创建的,我们在 Fabric SDK 节点 API 中没有等效 API 函数的情况下使用该工具:

if(!fs.existsSync('./tmp/')) {
  fs.mkdirSync('./tmp');
}

获得配置后,我们需要将它以 protobuf 格式转储到一个文件中:

fs.writeFileSync('./tmp/config.pb', configuration_block.config.toBuffer()); 

接下来,我们需要使用configtxlator将这个配置解码成 JSON 格式。我们这样做纯粹是为了方便,因为解析 JSON 并对其应用我们想要的配置更改更容易:

cproc.execSync('configtxlator proto_decode --input ./tmp/config.pb --type common.Config | jq . > ./tmp/config.json');

这导致在temporary文件夹中创建一个名为config.json的文件。如果您查看该文件的内容,您将看到通道的底层配置结构以及可以更新的各种属性。

现在我们需要将新的(导出实体)组织的配置附加到它上面。后者包含在文件exportingEntityOrg.json中,在本节前面使用configtxgen工具创建并保存到network/channel-artifacts。我们使用jq工具创建新的附加配置modified_config.json,如下所示:

cproc.execSync('jq -s \'.[0] * {"channel_group":{"groups":{"Application":{"groups": {"ExportingEntityOrgMSP":.[1]}}}}}\' ./tmp/config.json ../network/channel-artifacts/exportingEntityOrg.json > ./tmp/modified_config.json');

如果查看modified_config.json的内容,会发现它在结构上与config.json非常相似;不同之处在于,它包含 5 个组织的定义,而后者只包含 4 个。我们现在将这个新配置转换成 protobuf 格式(modified_config.pb),这样configtxlator就可以处理它了:

cproc.execSync('configtxlator proto_encode --input ./tmp/modified_config.json --type common.Config --output ./tmp/modified_config.pb'); 

注意,我们使用相同的 protobuf 模式(常见的。Config ),我们用它来解码从订购者那里获得的配置。

最后,我们将使用configtxlator来计算原始配置和新配置之间的增量(或差异):

cproc.execSync('configtxlator compute_update --channel_id ' + channel_name + ' --original ./tmp/config.pb --updated ./tmp/modified_config.pb --output ./tmp/exportingEntityOrg_update.pb'); 

生成的 protobuf exportingEntityOrg_update.pb包含了exportingentityOrg的完整定义和指向现有 4 个组织的指针。这对于信道配置更新来说已经足够了,因为其他组织的完整定义已经包含在先前的配置块中(在我们的示例中是 genesis 块)。

现在,我们要做的就是读取增量配置,并从现有的四个组织中获取管理员签名。其代码类似于我们在通道创建阶段检查的代码:

config = fs.readFileSync('./tmp/exportingEntityOrg_update.pb'); 
var signature = client.signChannelConfig(config); 
signatures.push(signature); 

我们现在需要做的就是创建一个更新请求,并将其发送给订购者:

let tx_id = client.newTransactionID(); 
var request = { 
  config: config, 
  signatures : signatures, 
  name : channel_name, 
  orderer : orderer, 
  txId  : tx_id 
}; 
client.updateChannel(request); 

请求结构可以包含配置或信封字段。后者具有共性。Envelope protobuf 格式,是我们刚刚创建的配置的包装器。织物订购者将接受任何一种。使用 envelope 代替 config 留给读者作为练习。

要推送通道配置更新,只需运行:

node run-upgrade-channel.js 

请确保来自 第 5 章【公开网络资产和交易的原始 4-org 网络已启动并运行,并且渠道创建步骤(参见middleware/createTradeApp.js中的示例)已经执行。

将新组织添加到网络中

新的组织通过配置更新被逻辑地添加到频道中。要将其实际添加到我们的交易网络中,并使其参与共享分类帐交易,我们需要:

  • 将出口实体组织的同行加入 tradechannel
  • 在新添加的对等体上安装当前版本的链码

好消息是这里没有什么新的东西要做。我们已经实现了这两个过程的功能(分别是join-channel.js中的joinChannelinstall-chaincode.js中的installChaincode,我们只需要代表新组织的资源来执行它们。

在运行这些步骤之前,我们必须增加中间件使用的网络配置。之前,我们在middleware文件夹中使用config.json来表示 4-组织网络。我们现在将在同一个文件夹中用config_upgrade.json替换它。这个文件包含的只是 trade-network 中一个名为exportingentityorg的额外属性(中间件代码将通过它来识别我们的新组织),如下所示:

"exportingentityorg": { 
  "name": "peerExportingEntityOrg", 
  "mspid": "ExportingEntityOrgMSP", 
  "ca": { 
    "url": "https://localhost:11054", 
      "name": "ca-exportingentityorg" 
  }, 
  "peer1": { 
    "requests": "grpcs://localhost:11051", 
    "events": "grpcs://localhost:11053", 
    "server-hostname": "peer0.exportingentityorg.trade.com", 
    "tls_cacerts": "../network/crypto-config/peerOrganizations/exportingentityorg.trade.com/peers/peer0.exportingentityorg.trade.com/msp/tlscacerts/tlsca.exportingentityorg.trade.com-cert.pem" 
  } 
} 

请注意,前面指出的端口与 docker-compose-exportingentityorg . YAML 文件中指定的端口相匹配,我们使用该文件来启动该组织的 MSP 和 peer。证书的路径与本节前面使用cryptogen生成的路径相匹配,名称与configtx.yaml中指定的名称相匹配。该组织只有一个对等体,这正是我们在后一个文件中指定的。

为了确保中间件功能加载正确的配置,我们需要将constants.js中的networkConfig变量的值从config.json改为config_upgrade.json。我们在文件new-org-join-channel.js中这样做如下:

var Constants = require('./constants.js'); 
Constants.networkConfig = './config_upgrade.json';

现在,我们准备为属于导出实体的组织的单个对等体运行通道加入过程。new-org-join-channel.js中的代码如下:

var joinChannel = require('./join-channel.js'); 
Client.addConfigFile(path.join(__dirname, Constants.networkConfig)); 
var ORGS = Client.getConfigSetting(Constants.networkId); 
joinChannel.joinChannel('exportingentityorg', ORGS, Constants); 

joinChannel的调用具有加入对等体的效果,对等体的细节在config_upgrade.jstradechanneltrade-network:exportingentityorg:peer1部分中指定。要执行此操作,只需运行以下命令:

node new-org-join-channel.js 

新对等体现在是通道的一部分,并且最终将通过 gossip 协议从现有网络对等体同步通道的共享分类帐的内容。

同样,我们可以通过调用install-chaincode.js中的installChaincode函数在这个对等体上安装 chaincode。但是碰巧的是,我们想在这个时候展示链码升级能力。因此,我们可以直接在所有 5 个对等体上安装新版本,而不是运行安装过程两次。我们将在下一节描述这个过程。

智能合同和政策更新

正如我们在本章的前面部分所观察到的,绑定共享通道上的对等体的智能契约会因为各种原因而发生变化,这些原因包括代码修复和参与者不断发展的需求。不管什么原因,Hyperledger Fabric 提供的机制和变化的语义保持不变。这个机制就是我们将在本节中演示的。

至少在区块链的结构视图中,与智能合约密切相关的是背书策略,必须满足该背书策略才能将交易结果提交到共享分类帐。正如我们将看到的,可以升级智能合同的相同机制也可以用于修改背书策略。

修改链码逻辑

让我们首先考虑一个需要我们更新(或升级)贸易链代码的场景。我们刚刚在上一节中添加了一个新的组织,这需要对 chaincode 进行某些更改。作为一个例子,让我们考虑chaincode/src/github.com/trade_workflow/tradeWorkflow.go中的acceptTrade函数中的以下代码片段:

// Accept a trade agreement 
func (t *TradeWorkflowChaincode) acceptTrade(stub shim.ChaincodeStubInterface, creatorOrg string, creatorCertIssuer string, args []string) pb.Response { 
  // Access control: Only an Exporter Org member can invoke this transaction 
  if !t.testMode && !authenticateExporterOrg(creatorOrg, creatorCertIssuer) { 
    return shim.Error("Caller not a member of Exporter Org. Access denied.") 
  } 

前面的访问控制逻辑规定只有出口商组织的成员可以接受交易。在我们早期的 4 组织网络中,这是有意义的,因为出口商和出口商的银行都是一个组织的一部分,我们依靠更高层的进一步访问控制来区分银行家和他们的客户,以便执行链码操作。但是现在我们已经添加了一个独立于银行的组织来满足出口商的需求(现在将出口商称为出口实体),我们应该相应地更改访问控制逻辑。并且这不是在访问控制逻辑中需要这种修改的唯一功能。

因此,我们需要生产新版本的链码。在我们的代码库中,这可以在chaincode/src/github.com/trade_workflow_v1/中找到。代码的内容,除了一些访问控制过滤规则之外,看起来与原始版本几乎相同。让我们看看chaincode/src/github.com/trade_workflow_v1/tradeWorkflow.goacceptTrade函数的类似代码片段:

// Accept a trade agreement 
func (t *TradeWorkflowChaincode) acceptTrade(stub shim.ChaincodeStubInterface, creatorOrg string, creatorCertIssuer string, args []string) pb.Response { 
  // Access control: Only an Exporting Entity Org member can invoke this transaction 
  if !t.testMode && !authenticateExportingEntityOrg(creatorOrg, creatorCertIssuer) { 
    return shim.Error("Caller not a member of Exporting Entity Org. Access denied.") 
  } 

请注意,功能authenticateExporterOrg已被替换为authenticateExportingEntityOrg。如果您查看accessControlUtils.go文件的内容,您会注意到已经添加了后一个函数的定义。

在涉及各种组织的实际应用中,链代码的更改必须通过协作和协商进行,通过带外机制传递给不同的利益相关者,经过检查、审核和测试,然后才可以部署到网络中。

链代码中的依赖性升级

访问控制逻辑并不是我们在链代码中唯一需要改变的东西。我们使用了一个有些做作的场景,其中在只有早期版本的 Fabric(比如 1.0 版)可用时创建了初始版本的 chaincode。如果您检查逻辑以提取发出事务的组织的 MSP 标识以及颁发给链码事务提交者的证书中的公用名,那么这是使用标准 Go 库手动完成的。这在下面的代码片段中进行了说明,代码片段位于chaincode/src/github.com/trade_workflow/accessControlUtils.go中的getTxCreatorInfo函数中:

creatorSerializedId := &msp.SerializedIdentity{} 
err = proto.Unmarshal(creator, creatorSerializedId) 
...... 
certASN1, _ = pem.Decode(creatorSerializedId.IdBytes) 
cert, err = x509.ParseCertificate(certASN1.Bytes) 
...... 
return creatorSerializedId.Mspid, cert.Issuer.CommonName, nil 

当 Fabric 平台升级到 1.1 版时,实现了一个名为 cid 的新包来执行前面的操作,并隐藏 protobuf 结构和证书解析的细节。为了使我们的链码更干净,更符合结构的变化,有必要升级我们前面的逻辑,以使用新的包。这就是我们在chaincode/src/github.com/trade_workflow_v1/accessControlUtils.go的升级版 chaincode 中所做的:

import ( 
  ...... 
  "github.com/hyperledger/fabric/core/chaincode/lib/cid" 
  ...... 
) 
...... 
func getTxCreatorInfo(stub shim.ChaincodeStubInterface) (string, string, error) { 
  ...... 
  mspid, err = cid.GetMSPID(stub) 
  ...... 
  cert, err = cid.GetX509Certificate(stub) 
  ...... 
  return mspid, cert.Issuer.CommonName, nil 
} 

分类帐重置

链码升级类似于实例化,两者都会导致执行Init函数。在 chaincode 的初始版本中,许多分类帐值被初始化,但是除非我们改变该逻辑,否则这些初始值将覆盖分类帐的当前状态。因此,我们向chaincode/src/github.com/trade_workflow_v1/tradeWorkflow.go中的Init函数添加代码以模拟无操作,但我们也保持原始逻辑不变,以确保如果业务需要,可以在升级期间覆盖值,如以下代码片段所示:

func (t *TradeWorkflowChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 
  ...... 
  // Upgrade Mode 1: leave ledger state as it was 
  if len(args) == 0 { 
    return shim.Success(nil) 
  } 
  // Upgrade mode 2: change all the names and account balances 
  if len(args) != 8 { 
    ...... 

背书政策更新

我们最初的交易签署政策要求 4 个组织中的每一个组织的成员签署一个链码调用交易。现在我们已经添加了一个新的组织,我们必须更新该策略,要求这 5 个组织中的每一个都有一名成员签名。在middleware文件夹中,这个新策略在constants.js中定义如下:

var FIVE_ORG_MEMBERS_AND_ADMIN = [{ 
  role: { 
    name: 'member', 
    mspId: 'ExporterOrgMSP' 
  } 
}, { 
  role: { 
    name: 'member', 
    mspId: 'ExportingEntityOrgMSP' 
  } 
}, { 
  role: { 
    name: 'member', 
    mspId: 'ImporterOrgMSP' 
  } 
}, { 
  role: { 
    name: 'member', 
    mspId: 'CarrierOrgMSP' 
  } 
}, { 
  role: { 
    name: 'member', 
    mspId: 'RegulatorOrgMSP' 
  } 
}, { 
  role: { 
    name: 'admin', 
    mspId: 'TradeOrdererMSP' 
  } 
}]; 

var ALL_FIVE_ORG_MEMBERS = { 
  identities: FIVE_ORG_MEMBERS_AND_ADMIN, 
  policy: { 
    '5-of': [{ 'signed-by': 0 }, { 'signed-by': 1 }, { 'signed-by': 2 }, { 'signed-by': 3 }, { 'signed-by': 4 }] 
  } 
}; 

要在我们的中间件中切换背书策略,我们只需要将constants.js中的TRANSACTION_ENDORSEMENT_POLICY变量的值从ALL_FOUR_ORG_MEMBERS更改为ALL_FIVE_ORG_MEMBERS

升级贸易渠道的链码和背书政策

现在,我们准备执行升级过程,这将需要两个步骤:

  1. 在网络对等体上安装新版本的链码
  2. 渠道链码和背书政策的升级

执行这些步骤的代码可以在middleware/upgrade-chaincode.js中找到,只需要调用我们已经实现的函数(参见 第 5 章公开网络资产和交易)。以下代码片段显示了我们需要为安装做些什么:

var Constants = require('./constants.js'); 
var installCC = require('./install-chaincode.js'); 
Constants.networkConfig = './config_upgrade.json'; 
Constants.TRANSACTION_ENDORSEMENT_POLICY = Constants.ALL_FIVE_ORG_MEMBERS; 
installCC.installChaincode(Constants.CHAINCODE_UPGRADE_PATH, Constants.CHAINCODE_UPGRADE_VERSION, Constants); 

请注意,在上述代码中,使用了 5 个组织的网络配置以及 5 个组织的认可策略。链码的新路径和版本在constants.js中设置如下:

var CHAINCODE_UPGRADE_PATH = 'github.com/trade_workflow_v1'; 
var CHAINCODE_UPGRADE_VERSION = 'v1'; 

该路径相对于存储库中的 chaincode/src 文件夹,因为GOPATH被临时设置为chaincode/文件夹被复制到的位置(参见constants.jsinstall-chaincode.js)。版本设置为 v1,而不是初始版本 v0。

您选择的链码版本 IDMUST在链码的生命周期内是唯一的;也就是说,它必须没有用于任何以前的版本。

触发升级是下一步,从开发人员的角度来看,这几乎与实例化步骤相同:

var instantiateCC = require('./instantiate-chaincode.js'); 
instantiateCC.instantiateOrUpgradeChaincode( 
  Constants.IMPORTER_ORG, 
  Constants.CHAINCODE_UPGRADE_PATH, 
  Constants.CHAINCODE_UPGRADE_VERSION, 
  'init', 
  [], 
  true, 
  Constants 
); 

正如我们在前面看到的,我们通过传递一个空的参数列表来选择离开当前的分类帐状态。在instantiate-chaincode.js中的函数instantiateOrUpgradeChaincode中,建议建立后,调用channel.sendUpgradeProposal(request, 300000)而不是channel.sendInstantiateProposal(request, 300000)向订购者发送请求。与实例化的情况一样,我们注册事件侦听器来告诉我们请求是否成功。

要推动链码升级,请运行:

node upgrade-chaincode.js 

要测试新的链代码,请运行:

node five-org-trade-scenario.js 

这将运行一系列交易操作(对链码的调用和查询),涉及从交易请求到发货的最终付款的各方。

平台升级

您的分布式区块链应用程序必须预测并支持对平台组件所做的更改。重点关注我们在示例贸易网络中创建和发布的组件,这些组件包括 Fabric peer、orderer 和 CA(或 MSP。)就像应用程序链代码会因缺陷和新需求而发生变化一样,平台也会随着时间的推移而变化。Fabric 自 2015 年末诞生以来,已经经历了多次变化,每次变化都被推送到一个新版本的升级,目前的版本是 1.1。每当平台组件升级时,您都需要在不中断应用程序生命周期的情况下替换正在运行的系统中的那些组件。在本节中,我们将演示如何做到这一点。

您可以在不同的配置中运行您的网络组件,一种方式是使用 docker 容器,这是我们在本书中演示的方法。要升级 docker 容器中运行的平台组件,首先需要为各种组件生成新的映像。这可以通过从 Docker Hub 下载相关图像或者下载源代码并使用 make docker 本地构建图像来实现;后一种方法是我们在本书中遵循的。要查看下载到您系统的 Hyperledger Fabric 图像的完整列表,您可以运行以下命令:

docker images | grep hyperledger/fabric 

您将看到一长串图像条目,其中大部分是重复的,最新的标签是一个带有特定标签名称的图像的指针。由于我们在网络文件夹(docker-compose-e2e.yamlbase/docker-compose-base.yamlbase/peer-base.yaml)中的 docker-compose YAML 文件仅依赖于 fabric-peer、fabric-order 和 fabric-ca 的图像,因此让我们只检查这些图像:

hyperledger/fabric-peer    latest    f9224936c8c3    2 weeks ago    187MB 
hyperledger/fabric-peer    x86_64-1.1.1-snapshot-c257bb3    f9224936c8c3    2 weeks ago    187MB 
hyperledger/fabric-orderer    latest    5de53fad366a    2 weeks ago    180MB 
hyperledger/fabric-orderer    x86_64-1.1.1-snapshot-c257bb3    5de53fad366a    2 weeks ago    180MB 
hyperledger/fabric-ca    latest    39fdba61db00    2 weeks ago    299MB 
hyperledger/fabric-ca    x86_64-1.1.1-snapshot-e656889    39fdba61db00    2 weeks ago    299MB 

当您运行docker images命令时,您会看到类似前面的内容。此处列出的 Docker 映像是从 Fabric 和 Fabric CA 源代码的 1.1 版分支本机构建的。如果您下载不同版本的源代码并使用 make docker 构建图像,您将会看到前面每个组件的第三个图像条目,并且您的最新图像标签将会链接到您刚刚创建的那个。

我们将经历下面的例子,其中贸易网络的订购者和对等者被升级。我们将把升级 fabric-ca 作为一项练习留给用户。要在正在运行的应用程序中做到这一点,您需要执行以下一系列步骤:

  1. 下载或构建新版本的平台组件映像
  2. 停止组件
  3. (可选)为安全起见,请备份您的分类帐内容
  4. 停止运行链码容器

  5. 从系统中删除链码容器图像

  6. 确保 docker-compose YAML 文件中引用的图像标签链接到组件的新版本
  7. 启动组件

您还可以选择依次停止、升级和启动每个组件,而不是一次全部停止、升级和启动。在升级过程中,您需要停止对系统的所有传入请求,这应该是关闭应用程序 web 服务器的简单事情。

在代码存储库中的 network/trade.sh 中的 upgradeNetwork 函数中有以这种方式升级我们的贸易网络的示例代码。这里,我们假设用户将会:

  • 使用-i开关将新的图像标签(例如前面列表中的x86_64-1.1.1-snapshot-c257bb3)作为命令行参数传递,或者
  • 将最新标签链接到新图像

在调用函数之前。现在我们必须阻止订购者和同行:

COMPOSE_FILE=docker-compose-e2e.yaml 
...... 
COMPOSE_FILES="-f $COMPOSE_FILE" 
...... 
docker-compose $COMPOSE_FILES stop orderer.trade.com 
...... 
for PEER in peer0.exporterorg.trade.com peer0.importerorg.trade.com peer0.carrierorg.trade.com peer0.regulatororg.trade.com; do 
  ...... 
  docker-compose $COMPOSE_FILES stop $PEER 
  ...... 
done 

正如我们在前面的代码中看到的,用于启动网络的 docker-compose YAML 文件也必须用于停止单个组件。

前面的示例假设只有前 4 个组织是网络的一部分。

一旦容器停止,我们可以选择备份分类帐数据,如下所示:

LEDGERS_BACKUP=./ledgers-backup 
mkdir -p $LEDGERS_BACKUP 
...... 
docker cp -a orderer.trade.com:/var/hyperledger/production/orderer $LEDGERS_BACKUP/orderer.trade.com 
...... 
for PEER in peer0.exporterorg.trade.com peer0.importerorg.trade.com peer0.carrierorg.trade.com peer0.regulatororg.trade.com; do 
  ...... 
  docker cp -a $PEER:/var/hyperledger/production $LEDGERS_BACKUP/$PEER/ 
  ...... 
done 

对等方以及订购方的分类帐内容现在备份到您本地机器的分类帐备份文件夹中。

现在,我们应该删除所有链码映像,因为新的链码映像需要由新的结构对等映像创建,而旧映像的存在会阻止创建:

for PEER in peer0.exporterorg.trade.com peer0.importerorg.trade.com peer0.carrierorg.trade.com peer0.regulatororg.trade.com; do 
  ...... 
  CC_CONTAINERS=$(docker ps | grep dev-$PEER | awk '{print $1}') 
  if [ -n "$CC_CONTAINERS" ] ; then 
    docker rm -f $CC_CONTAINERS 
  fi 
  CC_IMAGES=$(docker images | grep dev-$PEER | awk '{print $1}') 
  if [ -n "$CC_IMAGES" ] ; then 
    docker rmi -f $CC_IMAGES 
  fi 
  ...... 
done 

注意,我们必须首先检查 chaincode 容器是否正在运行,如果它们正在运行,就停止它们,否则就不能删除图像。

现在我们可以重新启动停止的订购者和对等容器。当运行 docker-compose up 时,orderer 和 peer 容器将用新的映像启动:

docker-compose $COMPOSE_FILES up --no-deps orderer.trade.com 
...... 
for PEER in peer0.exporterorg.trade.com peer0.importerorg.trade.com peer0.carrierorg.trade.com peer0.regulatororg.trade.com; do 
  ...... 
  docker-compose $COMPOSE_FILES up --no-deps $PEER 
  ...... 
done 

您可以通过以下任一方式运行脚本,一次性运行整个升级过程:

./trade.sh upgrade [-i <imagetag>] 

如前所述,如果没有指定<imagetag>,它将默认为 latest。

现在,您可以继续运行您的分布式交易应用程序。请注意,平台的变化也可能伴随着链码和 SDK API 的变化,这可能需要升级链码和/或中间件。正如我们在前面几节中展示的例子一样,读者在应用和网络生命周期的任何时候都不应该完全具备升级应用和底层区块链平台的能力。

系统监控和性能

现在,您已经构建了自己的应用程序,并建立了各种流程和机制,以应对其生命周期中的变化。一个额外的但同样重要的过程是监控和绩效评估,您必须随时准备好并执行。您为现实世界的用户和机构构建的任何生产应用程序都必须满足特定的性能目标,才能对其用户有用,也就是说,对应用程序的利益相关者有用。因此,了解您的应用程序如何执行并尝试提高其性能是一项关键的维护任务;在这项任务中的任何疏忽都可能导致您的应用程序寿命缩短。

系统性能测量和分析的艺术(和科学)是一组广泛而深入的主题,我们无意在本书中深入或详尽地涵盖这些主题。为了获得这样的覆盖面,鼓励感兴趣的读者阅读关于该主题的其他规范文本(例如,https://www . Amazon . com/Systems-Performance-Enterprise-Brendan-Gregg/DP/0133390098)。)相反,我们将提供性能测量和深入了解区块链应用程序所需的预览,并提供一些关于开发人员或系统管理员可用于这些目的的工具和技术的提示和建议。

概括地说,系统性能维护包括三个大致连续的任务类别,尽管这些任务可以在系统的生命周期内循环重复:

  • 观察和测量
  • 评估(或分析)和获得洞察力(或理解)
  • 为改进而进行的重组、重新设计或重新实施

在本节的讨论中,我们将主要关注以下几个方面:

  • 在织物应用中,测量什么是重要的
  • Fabric 应用程序开发人员或管理员可用于测量的机制
  • 应用程序设计人员和开发人员应该了解的 Fabric 的性能抑制方面

测量和分析

在具体讨论 Hyperledger Fabric 之前,让我们了解一下测量和分析对于分布式系统意味着什么,区块链应用程序就是一个例子。该过程从全面理解系统的体系结构、其各种组件以及这些组件之间的耦合程度和性质开始。下一步是建立监控各种组件的机制,并持续或定期收集与性能有关的数据属性。必须收集这些数据,并将其传送到一个模块,然后该模块可以对其进行分析,以生成系统性能的有意义的表示,并可能提供对应用程序的工作方式及其现有低效率的更多洞察。所分析的数据还可以用于确保系统以期望的性能水平工作,并检测何时不工作,这对于面向用户的系统是非常重要的(如果不是关键的话)。

这种技术和过程在分布式系统分析领域是众所周知的,在移动分析领域也是如此(这可以被认为是前者的特例)。)代理可以被配置为主动或被动地观察或监视系统组件:在前者中,系统可以被装备(例如,通过插入特殊的数据收集代码)以使它们自我监视它们的活动并收集信息,而在后者中,数据收集可以由被监视组件外部的一个软件来完成。存在一个管道来持续或定期地将这些数据传送到中央存储库,在那里可以累积数据以供以后处理,或者立即处理和使用。管道可能会修改数据,使其也可用于分析。在数据分析的术语中,这个管道通常被称为提取-转换-加载 ( ETL )。如果数据生成的数量和频率非常高,并且如果数据源的数量非常大,这样的分析也被称为大数据分析

ETL 流程或大数据分析超出了本章和本书的范围,但对于认真的区块链开发人员或管理员来说,有一些框架可以执行此类分析,或者是针对后端配置有服务器和数据库的分布式系统(以及一个结构区块链应用程序就是一个例子),如 Splunk(https://www . Splunk . com/en _ us/solutions/solution-areas/business-Analytics . html)或 apte ligent(【http://www.apteligent.com/】,或者是针对 Tealeaf ( 【T4 同样的框架也可以用来监控和分析区块链应用程序。

在织物应用中,我们应该测量或了解什么

基于 Hyperledger Fabric 及其相关工具构建的应用程序实际上是一个分布式事务处理系统

区块链应用相对于传统交易处理应用

想想传统的事务处理系统是什么样子的。您将在后端拥有一个数据库来存储、处理和提供数据;该数据库可以是集中式的或分布式的,在后一种情况下,维护副本或分区。在数据库前面,你会有一个或多个 web 服务器或应用服务器来管理和运行你的应用逻辑;再往前,你会有一个或多个与用户互动的界面。

类似地,Fabric 区块链应用程序让对等方维护一个共享的复制分类帐,相当于一个数据库。智能合同代码类似于传统数据库管理系统中的存储过程和视图。中间件和应用程序服务器,我们已经为我们的贸易应用程序演示了其架构和工作方式,可以等同于甚至托管于传统的应用程序服务器。最后,我们可以为用户交互设计 web 界面,就像我们为传统的事务处理应用程序所做的那样。当然,我们使用curl作为替代品来测试我们的贸易用例。

绩效分析的指标

因此,影响区块链应用程序性能的因素与影响传统的基于 DBMS 的事务处理应用程序的因素相似。首先,我们必须持续监控托管应用程序组件的硬件资源的健康状况。对于每一台运行 peer、orderer 或 CA 的机器,我们需要跟踪基本的健康指标,例如 CPU 使用率、内存使用率、磁盘 I/O 速度、网络带宽、延迟和抖动,以及可用的存储空间(这并不是一个详尽的列表)。这些因素,尤其是处理密集型系统的 CPU 使用率,决定了应用程序是否运行在最佳性能水平。

正如我们在本书中看到的,结构网络可以以多种配置启动,从每个对等点和订购者的单个专用机器(物理或虚拟)到在隔离的docker容器中运行每个组件的单机设置(就像本书中的贸易网络设置)。在后一种情况下,您不仅需要监控机器的健康状况,还需要监控每个容器的健康状况。还要记住,每个 Fabric chaincode 实例总是在 docker 容器中运行,而不是在专用机器上运行。此外,在理解(或分析)应用程序时,应用程序组件的 CPU、内存和 I/O 使用是最重要的。在本节的后面,我们将研究一些工具来测量容器和应用程序的性能。

从外部因素转到应用程序本身,结构应用程序的性能(就像任何其他事务处理应用程序一样)由两个特征指标定义:

  • 吞吐量:这是您的系统在单位时间内可以产生的事务数量。由于 Fabric 是一个松散耦合的系统,并且一个事务有多个阶段(参见 第 5 章展示网络资产和事务,例如在我们的交易场景中),我们可以测量不同阶段的吞吐量。但是,从客户构建交易建议以供认可到收到指示分类帐承诺的事件,总吞吐量提供了应用程序执行情况的最佳总体情况。另一方面,如果我们只想测量订购者的吞吐量,我们需要收集客户机向订购者发送一个认可的事务信封并返回一个响应的部分的统计信息。
  • 延迟:由于大多数 Fabric 应用程序最终都是面向用户的,因此在现实场景中,重要的不仅仅是处理能力或数量,还有每个事务需要多长时间。与吞吐量一样,我们可以测量不同的延迟—链代码执行和认可、订购和块创建、交易验证和分类账提交,甚至事件发布和订阅。我们还可以测量组件间的通信延迟,以了解通信基础设施的局限性。

还有其他重要的东西需要度量,比如跨对等体同步分类帐状态所花费的时间(使用 gossip 协议),但是从事务处理的角度来看,前面两个度量是最重要的。当我们度量这些因素时,我们可以了解整个应用程序的执行情况,以及它的组成部分,如对等点中的 ESCC 和 VSCC 以及订购点中的 Kafka 服务。

织物应用中的测量和数据收集

既然我们知道应该测量什么,让我们看一些实际测量和数据收集的例子。我们将使用我们的单虚拟机(Linux)、多码头集装箱贸易网络进行演示,并让读者将这些方法(借助更全面的测量文本)推广到其他设置。

收集运行状况和容量信息

获取 CPU、内存和系统上其他活动信息的标准方法是检查/proc中的信息。此外,Linux 中有一系列工具可以用来获取特定的信息。sysstat包包含了其中的许多,例如,iostat收集 CPU 和 I/O 统计,pidstat收集每个进程的健康统计,sarsadc收集与cron作业类似的统计。仅举一个例子,在运行整个贸易网络的虚拟机上运行iostat和链码会产生两个虚拟硬盘的以下 CPU 信息和 I/O 统计信息:

Linux 4.4.0-127-generic (ubuntu-xenial)    05/28/2018    _x86_64_    (2 CPU) 

avg-cpu:  %user    %nice    %system    %iowait    %steal    %idle 
           0.31     0.01       0.26       0.11      0.00    99.32 

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn 
sda               1.11        16.71        11.00     688675     453396 
sdb               0.00         0.05         0.00       2014          0

类似地,vmstat工具提供了虚拟机范围的信息汇总,如下所示:

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st 
 0  0      0 2811496 129856 779724    0    0     7     5  127  342  0  1 99  0  0 

对于连续的每个进程的统计,您也可以使用众所周知的top命令,还有dstat,它也生成 CSV 格式的输出以便于使用。如果您想要将您的度量机制连接到 ETL 分析管道,那么以众所周知的格式进行全面的性能数据收集和报告的nmon工具(http://nmon.sourceforge.net/pmwiki.php)可能是理想的工具。

但是,我们还必须专门分析运行应用程序组件的容器。作为 Linux 性能计数器和分析工具,perf工具非常方便。它可以收集每个线程、每个进程和每个 CPU(或处理器)的概要文件。数据收集是通过使用带有不同开关的perf report命令来完成的,这导致数据被收集并存储在运行该命令的文件夹中的一个名为perf.data的文件中。然后可以使用perf report命令分析这些数据。此外,【https://bindfs.org/】()可用于将perf报告中的符号映射到 docker 容器中运行的流程。最后,perf stat可以用来收集系统范围的统计数据。维基(https://perf.wiki.kernel.org/index.php/Main_Page)给出了更多关于如何使用这个工具的信息。

分析容器和应用程序

我们的应用程序组件也必须进行概要分析,以生成指令级信息和调用堆栈,供我们分析,不仅是为了跟踪性能,也是为了调试应用程序缺陷。strace 工具可以用来记录正在运行的 docker 容器发出的系统调用。例如,获取订购者容器的流程 ID,如下所示:

docker inspect --format '{{ .State.Pid }}' orderer.trade.com

回想一下,我们的容器在 docker-compose YAML 文件中被命名为orderer.trade.com。输出将是一个进程 ID;姑且称之为<pid>。现在在那个过程上运行strace:

sudo strace -T -tt -p <pid>

您应该会看到一个连续的输出,如下所示:

strace: Process 5221 attached 
18:48:49.081842 restart_syscall(<... resuming interrupted futex ...>) = -1 ETIMEDOUT (Connection timed out) <0.089393> 
18:48:49.171665 futex(0x13cd758, FUTEX_WAKE, 1) = 1 <0.000479> 
18:48:49.172253 futex(0x13cd690, FUTEX_WAKE, 1) = 1 <0.000556> 
18:48:49.174052 futex(0xc420184f10, FUTEX_WAKE, 1) = 1 <0.000035> 
18:48:49.174698 futex(0xc42002c810, FUTEX_WAKE, 1) = 1 <0.000053> 
18:48:49.175556 futex(0x13cd280, FUTEX_WAIT, 0, {1, 996752461}) = -1 ETIMEDOUT (Connection timed out) <1.999684> 

要分析输出,请阅读规范的strace文档。请注意,该工具仅在 Linux 系统上可用。此外,在 docker-compose YAML 文件中,您可以配置一个容器在内部运行strace。以peer0.exporterorg.trade.com in network/base/docker-compose-base.yaml的容器定义为例。您可以增加它来启用strace,如下所示(添加的配置以斜体显示):

peer0.exporterorg.trade.com: 
  container_name: peer0.exporterorg.trade.com 
 *cap_add: 
   - SYS_PTRACE 
  security_opt: 
    - seccomp:unconfined* 

最后,对于更具体的 Fabric 平台和您在其上开发的应用程序的信息,可以求助于 Go profiling。Fabric 组件(peers、order 和 CAs)是用 Golang 编写的,链代码也是如此,找出程序的哪些部分使用了更多的时间和资源对于提高应用程序的质量至关重要。对于这样的剖析,我们可以使用pprof(https://golang.org/pkg/net/http/pprof/),Golang 的内置剖析器(https://blog.golang.org/profiling-go-programs)。(请确保您打算在其中运行 profiler 的系统上安装了go。)要捕获由调用图和图中各种函数的运行频率(相当于 CPU 使用率)组成的应用程序配置文件,pprof需要一个 Go 应用程序来运行 HTTP 服务器,如下所示:

import "net/http" 
http.ListenAndServe("localhost:6060", nil)

为了获得一个概要文件,我们可以使用go tool来访问这个服务器并获取数据。例如,如果您的应用程序在端口6060上运行服务器,您可以通过运行以下命令获得堆配置文件:

go tool pprof http://localhost:6060/debug/pprof/heap 

您可以在前面的命令中用适当的主机名或 IP 地址替换localhost。要获得 30 秒的 CPU 配置文件,请运行:

go tool pprof http://localhost:6060/debug/pprof/profile 

Hyperledger Fabric 为这种分析提供了内置支持(https://github . com/Hyperledger-archives/Fabric/wiki/Profiling-the-Hyperledger-Fabric),至少在结构对等端是如此。为了启用概要分析(或者运行 HTTP 服务器),我们需要适当地配置对等体(或者在我们的例子中,运行对等体的docker容器)。回想一下,在我们的示例贸易网络中,每个对等体的核心配置在network/base/peer-base.yaml中定义。请注意以下几行:

services: 
  peer-base: 
    image: hyperledger/fabric-peer:$IMAGE_TAG 
    environment: 
      ...... 
      - CORE_PEER_PROFILE_ENABLED=true 
      ...... 

还记得在network/base/docker-compose-base.yaml中定义了容器和主机之间的对等端口映射。出口商和进口商组织同业的示例如下:

peer0.exporterorg.trade.com: 
  ...... 
  ports: 
    ...... 
    - 7055:6060 
    ...... 
peer0.importerorg.trade.com: 
  ...... 
  ports: 
    ...... 
    - 8055:6060 
    ......

虽然在它们的容器中,配置文件服务器运行在端口6060上,但是在主机上,pprof将访问端口7055来捕获出口组织对等体的配置文件,访问端口8055来捕获进口组织对等体的配置文件。

作为一个例子,让我们捕获一个 30 秒钟的导出组织的对等体的 CPU 配置文件。我们可以启动贸易网络,并使用middleware/createTradeApp.js运行渠道创建和链码安装步骤。在不同的终端窗口中,我们可以运行:

go tool pprof http://localhost:7055/debug/pprof/profile 

这将最终在~/pprof中生成一个文件,并在您的控制台上显示如下内容:

Fetching profile over HTTP from http://localhost:7055/debug/pprof/profile 
Saved profile in /home/vagrant/pprof/pprof.peer.samples.cpu.006.pb.gz 
File: peer 
Build ID: 66c7be6d1f71cb816faabc48e4a498bf8052ba1b 
Type: cpu 
Time: May 29, 2018 at 5:09am (UTC) 
Duration: 30s, Total samples = 530ms ( 1.77%) 
Entering interactive mode (type "help" for commands, "o" for options) 
(pprof) 

最后,该工具留下一个pprof shell 来运行各种分析命令,以分析获得的转储。例如,要获得前五个最活跃的函数或 goroutines:

(pprof) top5 
Showing nodes accounting for 340ms, 64.15% of 530ms total 
Showing top 5 nodes out of 200 
      flat  flat%   sum%        cum   cum% 
     230ms 43.40% 43.40%      230ms 43.40%  runtime.futex /opt/go/src/runtime/sys_linux_amd64.s 
      30ms  5.66% 49.06%       30ms  5.66%  crypto/sha256.block /opt/go/src/crypto/sha256/sha256block_amd64.s 
      30ms  5.66% 54.72%       30ms  5.66%  runtime.memmove /opt/go/src/runtime/memmove_amd64.s 
      30ms  5.66% 60.38%       30ms  5.66%  runtime.usleep /opt/go/src/runtime/sys_linux_amd64.s 
      20ms  3.77% 64.15%      110ms 20.75%  runtime.findrunnable /opt/go/src/runtime/proc.go

tree命令以文本形式显示整个调用图,其中一部分如下所示:

(pprof) tree 
Showing nodes accounting for 530ms, 100% of 530ms total 
Showing top 80 nodes out of 200 
----------------------------------------------------------+------------- 
      flat  flat%   sum%        cum   cum%   calls calls% + context 
----------------------------------------------------------+------------- 
                                              70ms 30.43% |   runtime.stopm /opt/go/src/runtime/proc.go 
                                              50ms 21.74% |   runtime.notetsleep_internal /opt/go/src/runtime/lock_futex.go 
                                              40ms 17.39% |   runtime.ready /opt/go/src/runtime/proc.go 
     230ms 43.40% 43.40%      230ms 43.40%                | runtime.futex /opt/go/src/runtime/sys_linux_amd64.s 
----------------------------------------------------------+------------- 
                                              30ms   100% |   crypto/sha256.(*digest).Write /opt/go/src/crypto/sha256/sha256.go 
      30ms  5.66% 49.06%       30ms  5.66%                | crypto/sha256.block /opt/go/src/crypto/sha256/sha256block_amd64.s 
----------------------------------------------------------+------------- 

您也可以在网页上或通过生成文件以图形方式查看图表:

(pprof) png 
Generating report in profile001.png

以下示例显示了作为 PNG 图像生成的调用图:

图 9.4:调用图的一部分,表示在 30 秒内对等节点中执行的功能

这是调用图图像的一部分,每个方框代表一个函数,方框的大小表示该函数的频率(即运行该函数的评测样本数)。有向图的边表示从一个函数到另一个函数的调用,边表示进行这样的调用所花费的时间。

对于更多的pprof选项和分析工具,鼓励读者阅读文档。

测量应用程序性能

与前面描述的许多工具相比,测量应用程序的吞吐量和延迟没有那么神秘;它将涉及检测您的代码以收集和记录计时信息。在您的代码中,您将需要添加日志记录(或通信,对于远程报告)指令来记录何时执行特定的操作,或者添加适当的挂钩来根据需要启用或禁用数据收集。

测量延迟相当简单;您可以记录各种操作的时间,如客户提案提交、背书退回、订购方对请求的确认、分类账承诺时间以及收到事件的时间。收集大量事务的数据将使您能够获得总体事务延迟以及单个操作中发生的延迟。

要获得吞吐量信息,您需要生成不同数量和不同频率的事务负载。然后,您可以增加应用程序的负载,直到观察到的事务提交(或事件接收)频率低于事务负载生成频率。除此之外,您还需要像测量事务延迟那样检测代码。您可以更改不同的应用程序参数和特征,并运行此类吞吐量测量来确定应用程序和资源特征,以获得最佳性能。

给定我们可以使用本节描述的工具收集的所有信息,应用或网络设计人员可以进行高级分析,以确定系统的哪些部分(例如,从pprof调用图)运行良好,哪些部分是瓶颈。然后,可以尝试通过向“瓶颈”组件添加更多资源或重新实现系统以使这些组件更高效来补救性能限制。跨不同冗余资源的负载平衡是另一种广泛使用的保持高性能水平的技术。瓶颈检测和分析本身就是一个非常重要的主题,鼓励读者研究文本和学术论文以获得更好的理解。

织物工程性能指南

我们现在将从一般转向具体。在本节中,我们将对 Hyperledger Fabric 性能进行评论,讨论影响性能的平台的显著特征,并为开发人员提供从其应用程序中获取最佳性能的指南。

平台性能特征

到目前为止,本书的读者应该非常熟悉 Fabric 架构和事务管道。它是一个复杂的分布式系统,其性能取决于许多因素,从与结构交互的应用程序的体系结构到一致实施、事务大小、块大小、结构网络大小,以及底层硬件和物理网络介质的能力。

在写这本书的时候,性能测量显示,Fabric 可以产生每秒几千个事务的吞吐量(https://arxiv.org/abs/1801.10228)。).我们的读者需要记住的是,这些测量是使用执行非常简单操作的链码进行的,并且使用的应用和网络配置可能不代表典型的生产区块链网络。结构性能取决于具体的使用情形和底层硬件。例如,IBM Z 系统上的性能超过了其他平台,这是因为优化的 Go 编译器利用了硬件加速功能,例如加密算法和其他功能。良好的性能取决于充足资源的可用性和适当的配置;我们将在本节后面详细讨论配置。

系统瓶颈

对结构体系结构和事务阶段的简单检查将揭示可能的瓶颈组件。订购服务是一个主要而明显的例子。每笔交易都必须通过该服务,并被包含在一个块中,才有机会进行分类账承诺。但是请记住,仍然不能保证交易在提交时不会被拒绝。因此,在某种程度上,订购服务的性能为应用程序的性能设置了基准。显然,通过添加更多节点或增加每个节点的容量来增加订购者资源,可能会导致更好的性能。也可以使用其他排序机制来代替当前的 Fabric 默认值 Kafka。随着 Fabric 平台的发展,有望看到更好更快的订购算法。

另一个系统瓶颈存在于分类帐提交阶段,此时必须评估交易的背书真实性,并通过管理读和写冲突来加强数据库(分类帐)的一致性。加密操作本质上是繁重的,最近对 Fabric 的更改(例如在 v1.1 中)使得签名验证更加有效。作为开发人员或网络工程师,您可以通过最小化由于无效签名或事务间冲突而导致事务失败的可能性来提高性能。对于前者,在批准阶段和订单生成请求期间进行更好的验证可以降低失败的几率。

为了减少冲突,需要试验不同的块大小(记住,要检查块内事务之间的冲突)。尽管较大的块可能导致较高的吞吐量,但冲突可能会产生相反的效果。您还可以设计您的链代码,使不同调用事务之间发生冲突的可能性最小化。有关 Fabric 如何检测和处理块中的冲突的说明,请参见第 4 章使用 Golang 设计数据和事务模型,多版本并发控制部分。

*# 配置和调整

继续我们之前的讨论,您可以配置各种参数来优化您的应用程序的性能。这些参数中有许多是网络规模等系统要求的结果。但是您的核心结构配置中的一些参数(参见网络组件配置文件部分中的 第 3 章设置业务场景)可以调整以最大化性能。其中之一是块大小。可以通过实验(或者调整参数,直到获得最佳吞吐量和延迟)来确定应该为应用程序设置的精确的块大小(以字节和事务数为单位)。例如,对名为 Fabcoin 的加密货币应用程序的测量显示,最佳块大小为 2mb(https://arxiv.org/abs/1801.10228)。但是读者必须记住上一节中讨论的权衡,即一个块中的大量事务也可能导致较高的冲突率和事务拒绝。

您对交易背书策略的选择也将对性能产生重大影响。需要从签署对等体收集的签名越多,在提交时验证签名所需的时间就越长。此外,您的策略越复杂(即包含的子句越多),验证就越慢。现在这里要做一个权衡。更多的签署者和更复杂的策略通常会提供更高的保证(可靠性和信任度),但这是以性能(吞吐量和延迟)为代价的。因此,区块链应用程序管理员必须确定需要什么样的服务级别和信任级别,并相应地调整参数。

还有各种其他因素会影响 Fabric 应用程序的性能:这包括由于对等体之间的 gossip 协议导致的开销,以同步分类帐内容、您在应用程序中使用的通道数量以及事务生成率。在硬件级别,性能由组件可用的 CPU 数量和性能决定。一般来说,可以说增加 CPU 的数量可以提高组件和整个区块链网络的性能。如果你对更多细节感兴趣,关于这个主题的一篇好论文是 Hyperledger Fabric:一个用于许可区块链的分布式操作系统,EuroSys ' 18(https://dl.acm.org/citation.cfm?id=3190538),也可以在https://arxiv.org/pdf/1801.10228v1.pdf获得。

分类帐数据可用性和缓存

您可以通过优化存储在分类帐中的数据的可用性(即检索时间)来进一步提高分布式 Fabric 应用程序的性能。有几种策略可以做到这一点,我们将在这里概述其中的两种。

冗余提交对等体

为了增加客户端应用程序的数据可用性,可以在拓扑上更靠近客户端应用程序或访问数据的中间件组件部署额外的提交对等体(或多个对等体)。提交对等端接收新创建的块并维护最新的分类帐。它不参与背书过程,因此不接收来自客户的交易提案请求。因此,对等体的性能完全致力于维护分类帐和响应数据请求。就网络性能和系统安全配置而言,一个重要的考虑因素是选择和设置位置,使得提交对等体可以无障碍地连接到信道,并且网络吞吐量允许以低延迟接收新创建的块。

数据缓存

从对等体检索的数据可以存储在应用程序缓存中,以便将来对该数据的请求可以得到更快的服务。为了使缓存中的数据保持最新,应用程序必须监控底层分类帐中的变化,并用新的状态修改更新缓存的数据。如前所述,对等体向分类帐发出关于新提交的事务的事件通知。客户端可以截取该通知,并且通过检查事务的内容,客户端可以确定是否应该用新值更新缓存。

织物性能测量和基准测试

我们希望本书的这一部分已经让读者理解了为什么性能测量和分析是重要的,以及如何使他/她的应用程序提供足够的服务水平的一些线索。最后,我们将向读者介绍 Hyperledger 框架中现有的使用示例基准应用程序来测量性能(主要是吞吐量、延迟和资源利用率)的工具。

对于一个深入和全面的性能测量工具套件,你应该看看fabric-test(https://github.com/hyperledger/fabric-test/)。)特别是,PTE(https://github . com/hyperledger/fabric-test/tree/master/tools/PTE)是一个灵活的工具,可以使用样本链代码来驱动参数化的事务负载。

hyperledger Cello(https://www.hyperledger.org/projects/cello)不是一个性能测量工具,而是一个区块链供应和管理系统,支持在不同平台(虚拟机、云和容器集群)上启动网络。在尝试生产部署之前,它可以用来帮助启动、测试和测量示例网络。

hyperledger Caliper(https://www.hyperledger.org/projects/caliper)是另一个项目,该项目目前正在开发一个基准测试框架,以允许用户使用一组预定义的用例来测量特定区块链实施的性能,并生成报告。读者应记住,这些项目正在进行中,并应关注区块链绩效基准领域的研究推动的进一步发展。

摘要

维护和扩充区块链应用程序可能比创建和引导它更具挑战性,因为人们需要在监控和分析以及评估变化的影响方面有技巧。

在这一章中,我们描述了 Hyperledger Fabric 应用程序在其生命周期中不可避免地会发生变化的各种方式。我们以 canonical trade 应用程序为例,详细描述了如何将组织和同行添加到正在运行的网络中,如何扩充渠道配置,如何升级平台,以及如何在不对应用程序状态产生负面影响的情况下修改智能合同(chaincode)本身。

在本章的后半部分,我们概述了系统管理员开发人员可用于测量、分析和提高结构区块链应用程序性能的工具。我们还提供了设计系统以获得更好性能的指南。

随着进一步的研究和开发,Hyperledger 套件无疑将增加更多更好的系统变更和监控机制。对于典型的 Fabric 开发人员或管理员来说,本章应该是维护其生产应用程序的便捷指南。***


我们一直在努力

apachecn/AiLearning

【布客】中文翻译组