文档

Hyperledger Fabric 高可用篇

更新时间:

设计高可用的区块链应用程序

使用 Service Discovery

应用在向 peer 节点发送交易 proposal 时,或是连接通道内的所有 peer 节点,或是根据背书策略选择部分节点。默认情况下,SDK读取静态的 connection-profile 配置文件获取区块链节点信息,如果在应用运行期间网络发生变更(例如peer节点下线),或者背书策略的变化(例如一个新组织加入channel),应用程序无法感知,会造成连接失败或是背书验证失败。

为了解决这个问题,SDK 可向 Service Discovery 服务发送查询,动态获取指定通道和链码需要连接的 peer 节点列表,详情见 Fabric的服务发现机制

通过这种方式,SDK 可抓取与当前背书策略对应的 peer 组合,选择必要的 peer 节点发送 proposal。当某 peer 节点由于停机维护或故障下线时,SDK 也可自动尝试其他 peer 组合。由于阿里云 BaaS(Fabric)中,每个组织都会有2个 peer 背书节点,与 Service Discovery 相结合,可以保证1个组织中即使有1个peer下线也能正常处理业务交易。

在BaaS的SDK中使用该功能,首先在connection-profile配置文件目标通道的 peer 节点列表中,增加属性discover: true。 SDK将随机选取一个peer作为Service Discovery服务提供节点。

channels:
    mychannel:
        peers:
            peer1.org1.aliyunbaas.top:31111:
                chaincodeQuery: true
                endorsingPeer: true
                eventSource: true
                ledgerQuery: true
                discover: true
            peer2.org1.aliyunbaas.top:31121:
                chaincodeQuery: true
                endorsingPeer: true
                eventSource: true
                ledgerQuery: true
                discover: true
            peer1.org2.aliyunbaas.top:31111:
                chaincodeQuery: true
                endorsingPeer: true
                eventSource: true
                ledgerQuery: true
                discover: true
            peer2.org2.aliyunbaas.top:31121:
                chaincodeQuery: true
                endorsingPeer: true
                eventSource: true
                ledgerQuery: true
                discover: true

在应用程序中,调用sendTransactionProposalToEndorsers时指定DiscoveryOptions

  • setEndorsementSelector:

    • ENDORSEMENT_SELECTION_RANDOM: 随机选取满足背书策略的peer节点组合

    • ENDORSEMENT_SELECTION_LEAST_REQUIRED_BLOCKHEIGHT: 选取满足背书策略的,状态最新、块高最大的peer节点组合

  • setForceDiscovery:

    • true: 每一次发送proposal时都调用discovery服务获取peer列表,会有一定的资源消耗

    • false: 发送proposal时使用discovery服务缓存的peer列表,默认2分钟刷新一次

  • setInspectResults:

    • true: 关闭SDK 背书策略检查,由应用逻辑进行判断

    • false: SDK 自动进行背书策略检查,不满足抛出异常

示例代码如下。

import org.hyperledger.fabric.sdk.exception.ServiceDiscoveryException;
import org.hyperledger.fabric.sdk.ServiceDiscovery;
import static org.hyperledger.fabric.sdk.Channel.DiscoveryOptions.createDiscoveryOptions;
.....
.....
Channel.DiscoveryOptions discoveryOptions = Channel.DiscoveryOptions.createDiscoveryOptions();
discoveryOptions.setEndorsementSelector(ServiceDiscovery.EndorsementSelector.ENDORSEMENT_SELECTION_RANDOM); 
discoveryOptions.setForceDiscovery(false); 
discoveryOptions.setInspectResults(true); 
try {
     transactionPropResp = channel.sendTransactionProposalToEndorsers
         (transactionProposalRequest, discoveryOptions);
 } catch (ProposalException e) {
     System.out.printf("invokeTransactionSync fail,ProposalException:{}", e.getLocalizedMessage());
     e.printStackTrace();
 } catch (ServiceDiscoveryException e) {
     System.out.printf("ServiceDiscoveryException fail:{}", e.getLocalizedMessage());
     e.printStackTrace();
 } catch (InvalidArgumentException e) {
     System.out.printf("InvalidArgumentException fail:{}", e.getLocalizedMessage());
     e.printStackTrace();
 }
.....
.....
说明

建议应用将 Service Discovery 和 NOfEvents 配合使用,不需要等待每一个peer都发出transactionEvent 才算交易成功,避免因个别 peer 下线引发交易判定失败。

设置合适的超时时间

Java SDK中定义了各种超时的设置变量和默认值,源码地址:src\main\java\org\hyperledger\fabric\sdk\helper\Config.java

/**
 * Timeout settings
 **/
public static final String PROPOSAL_WAIT_TIME = "org.hyperledger.fabric.sdk.proposal.wait.time";
public static final String CHANNEL_CONFIG_WAIT_TIME = "org.hyperledger.fabric.sdk.channelconfig.wait_time";
public static final String TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME = "org.hyperledger.fabric.sdk.client.transaction_cleanup_up_timeout_wait_time";
public static final String ORDERER_RETRY_WAIT_TIME = "org.hyperledger.fabric.sdk.orderer_retry.wait_time";
public static final String ORDERER_WAIT_TIME = "org.hyperledger.fabric.sdk.orderer.ordererWaitTimeMilliSecs";
public static final String PEER_EVENT_REGISTRATION_WAIT_TIME = "org.hyperledger.fabric.sdk.peer.eventRegistration.wait_time";
public static final String PEER_EVENT_RETRY_WAIT_TIME = "org.hyperledger.fabric.sdk.peer.retry_wait_time";

public static final String PEER_EVENT_RECONNECTION_WARNING_RATE = "org.hyperledger.fabric.sdk.peer.reconnection_warning_rate";
public static final String GENESISBLOCK_WAIT_TIME = "org.hyperledger.fabric.sdk.channel.genesisblock_wait_time";

// Default values
/**
defaultProperty(PROPOSAL_WAIT_TIME, "20000");
defaultProperty(CHANNEL_CONFIG_WAIT_TIME, "15000");
defaultProperty(TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME, "600000");
defaultProperty(ORDERER_RETRY_WAIT_TIME, "200");
defaultProperty(ORDERER_WAIT_TIME, "10000");
defaultProperty(PEER_EVENT_REGISTRATION_WAIT_TIME, "5000
defaultProperty(PEER_EVENT_RETRY_WAIT_TIME, "500");
defaultProperty(PEER_EVENT_RECONNECTION_WARNING_RATE, "50");
defaultProperty(GENESISBLOCK_WAIT_TIME, "5000");
**/

用户可根据应用特点进行设置。

例如,应用发送交易到 peer 或者 orderer 时,节点由于故障或升级维护并不在线,SDK 需要等待一定时间直到 timeout 后再尝试其他的 peer 或者 orderer 节点。如果希望应用能够快速切换到健康的节点,在网络连接良好,交易处理速度较快的前提下,用户可适当减小PROPOSAL_WAIT_TIMEORDERER_WAIT_TIME的值。

  • 用户可以设置system属性来覆盖默认值。

    public static final String PROPOSAL_WAIT_TIME = "org.hyperledger.fabric.sdk.proposal.wait.time";
    private static final String PROPOSAL_WAIT_TIME_VALUE = "5000";
    public static final String ORDERER_WAIT_TIME = "org.hyperledger.fabric.sdk.orderer.ordererWaitTimeMilliSecs";
    private static final String ORDERER_WAIT_TIME_VALUE = "5000";
    
    static {
        System.setProperty(PROPOSAL_WAIT_TIME, PROPOSAL_WAIT_TIME_VALUE);
        System.setProperty(ORDERER_WAIT_TIME, ORDERER_WAIT_TIME_VALUE);
     }
  • 也可以在Java项目的config.properties中设置。

    ## The timeout for a proposal requests to endorser in milliseconds.
    org.hyperledger.fabric.sdk.proposal.wait.time = 5000
    ## The timeout for a transaction sent to orderer in milliseconds.
    org.hyperledger.fabric.sdk.orderer.ordererWaitTimeMilliSecs = 5000

配置GRPC消息大小限制

GRPC默认消息大小限制为4M,如果应用端与Fabric网络传输的消息超过4M则会报错.

rpc error: code = ResourceExhausted desc = grpc: received message larger than max (8653851 vs. 4194304)”。

在 peer,orderer 的属性中增加grpc.NettyChannelBuilderOption.maxInboundMessageSize (支持在 connection-profile 中配置 FABJ-480 )

NetworkConfig networkConfig = NetworkConfig.fromYamlFile(f);
// 从 connection-profile 中读取出配置后,插入下面的代码
for (String peerName : networkConfig.getPeerNames()) {
    Properties peerProperties = networkConfig.getPeerProperties(peerName);
    if (peerProperties == null) {
        peerProperties = new Properties();
    }
    peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 100*1024*1024);
    networkConfig.setPeerProperties(peerName, peerProperties);
}
for (String ordererName : networkConfig.getOrdererNames()) {
    Properties ordererProperties = networkConfig.getOrdererProperties(ordererName);
    if (ordererProperties == null) {
        ordererProperties = new Properties();
    }
    ordererProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 100*1024*1024);
    networkConfig.setPeerProperties(ordererName, ordererProperties);
}
  • 本页导读 (0)
文档反馈