All Products
Search
Document Center

Blockchain as a Service:Hyperledger Fabric High availability

Last Updated:Jan 12, 2024

Design highly-available blockchain applications

Use Service Discovery

The application sends transaction proposals to all peers in the channel or several nodes that are selected based on the endorsement policies. By default, the SDK obtains statically encoded information from connection-profile, including the node information. However, this implementation is not dynamically reactive to network changes (such as the peers that are temporarily offline). Static configurations also do not allow applications to react to changes of the endorsement policy itself (as might happen when a new organization joins a channel).

Service Discovery improves this process by having the peers compute the needed information dynamically and present it to the SDK in a consumable manner. For more information, see [How service discovery works in Fabric] (https://hyperledger-fabric.readthedocs.io/en/release-1.4/discovery-overview.html#how-service-discovery-works-in-fabric).

In this way, the SDK can select peers that satisfy the current endorsement policy, and the application knows which peers to send chaincode proposals to. When a peer goes offline due to downtime maintenance or failure, the SDK can automatically select other peers. In Alibaba Cloud BaaS (Fabric), each organization has two endorsing peers. Supported by Service Discovery, this service ensures that each organization can handle transactions properly even when one of the peers goes offline.

To use this feature in the SDK of BaaS, you need to go to the peer list of the target channels in connection-profile to add the discover: true property. The SDK will randomly select a peer as the service provider for 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

When the application calls sendTransactionProposalToEndorsers, specify DiscoveryOptions

  • setEndorsementSelector:

    • ENDORSEMENT_SELECTION_RANDOM: Randomly selects peers that satisfy the endorsement policy.

    • ENDORSEMENT_SELECTION_LEAST_REQUIRED_BLOCKHEIGHT: Selects the latest peers that satisfy the endorsement policy and have the highest ledger heights.

  • setForceDiscovery:

    • true: Every time the proposal is sent, the discovery service is called to obtain the peer list, which will consume some resources.

    • false: Uses the peer list cached by the discovery service when the proposal is sent. The peer list is updated every two minutes by default.

  • setInspectResults:

    • true: Disables the endorsement policy check and uses the application logic to check the endorsement policy.

    • false: Checks the endorsement policy automatically and throws exceptions when the condition is not fulfilled.

The sample code is as follows:

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();
 }
.....
.....
Note

Tips: We recommend that you use Service Discovery with NOfEvents, so that you do not have to wait until all peers send transactionEvent to complete a transaction. This ensures the success of a transaction even when a peer goes offline.

Set a proper timeout period

Multiple variables and default values or timeout settings are defined in the Java SDK. The source code URL is as follows: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");
**/

You can modify the timeout settings based on the features of the application.

Example:

  • When an application sends a transaction to a peer or orderer node and the node is offline due to a failure or upgrade, the SDK needs to wait until the timeout interval elapses before it connects to other peers or orderer nodes. When the network connection is stable and the transaction processing is fast, you can reduce the values of PROPOSAL_WAIT_TIME and ORDERER_WAIT_TIME, so the application can quickly switch to a healthy node.

You can set the system property to override the default value.

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);
 }

You can set the value in 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

Configure the maximum message size in gRPC

The default maximum message size that can be received by the server is 4 MB. If the server receives a message that exceeds this limit, it throws an exception.

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

Add grpc.NettyChannelBuilderOption.maxInboundMessageSize in the properties of the peer nodes and orderer nodes. Supports configuring FABJ-480 in connection-profile.

NetworkConfig networkConfig = NetworkConfig.fromYamlFile(f);
// After you obtain the configuration information from connection-profile, insert the following code
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);
}