Hyperledger Fabric high availability

更新时间:
复制 MD 格式

When a peer or orderer node goes offline, applications built on Hyperledger Fabric can stall or fail if the Java SDK does not know how to route around the outage. This guide covers three SDK-level configurations that keep your application running during node failures: Service Discovery for automatic peer failover, timeout tuning for faster node switching, and gRPC message size limits for large payloads.

Enable Service Discovery

By default, the Java SDK reads peer information from a statically encoded connection-profile. Static configuration has two limitations: it cannot react to peers going temporarily offline, and it does not reflect endorsement policy changes (for example, when a new organization joins a channel).

Service Discovery solves both problems. Instead of reading a fixed list, the SDK asks a peer to compute the current peer set and endorsement requirements dynamically. In Alibaba Cloud BaaS (Fabric), each organization runs two endorsing peers, so if one peer goes offline, the SDK automatically routes proposals to the other.

For more information on how this mechanism works, see How service discovery works in Fabric.

Configure the connection profile

Add discover: true to each peer entry in the target channel's peer list. The SDK randomly selects one of these peers as the Service Discovery provider.

Configure multiple peers per organization to prevent a single point of failure. Service Discovery uses these entries as the starting points for resolving the live peer set.

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

Send proposals with DiscoveryOptions

When calling sendTransactionProposalToEndorsers, pass a DiscoveryOptions object to control how the SDK selects peers and validates endorsements.

`setEndorsementSelector` — controls which peers are chosen to satisfy the endorsement policy:

ValueBehaviorWhen to use
ENDORSEMENT_SELECTION_RANDOMRandomly selects peers that satisfy the policyGeneral use; distributes load across peers
ENDORSEMENT_SELECTION_LEAST_REQUIRED_BLOCKHEIGHTSelects the minimum set of peers with the highest block heightWhen ledger consistency is critical

`setForceDiscovery` — controls whether the SDK calls the discovery service on every proposal or uses a cached peer list:

ValueBehaviorWhen to use
trueCalls the discovery service on every proposal (higher resource usage)When network topology changes frequently
falseUses the cached peer list; refreshes every 2 minutesDefault; suitable for stable networks

`setInspectResults` — controls endorsement policy validation:

ValueBehaviorWhen to use
trueSkips automatic policy checks; your application validates endorsementsWhen you need custom endorsement logic
falseSDK validates endorsements automatically and throws an exception if the policy is not satisfiedDefault; recommended for most applications

The following example uses random peer selection, cached discovery results, and application-side policy validation:

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();
}
// ...
Tip: Use Service Discovery together with NOfEvents so that a transaction completes as soon as the required subset of peers confirms it, rather than waiting for all peers to send a transactionEvent. This prevents transactions from stalling when a peer goes offline.

Tune timeout settings

When a peer or orderer node goes offline, the SDK waits until the configured timeout elapses before it tries another node. Reducing timeout values lets the SDK switch to a healthy node faster.

The Java SDK defines timeout constants in Config.java. Default values:

ConstantDefaultControls
PROPOSAL_WAIT_TIME20,000 msHow long the SDK waits for a peer to respond to a transaction proposal (client → peer)
ORDERER_WAIT_TIME10,000 msHow long the SDK waits for an orderer node to accept a transaction (peer → orderer)
CHANNEL_CONFIG_WAIT_TIME15,000 msChannel configuration update requests
TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME600,000 msTransaction cleanup timeout
ORDERER_RETRY_WAIT_TIME200 msDelay between orderer retry attempts
PEER_EVENT_REGISTRATION_WAIT_TIME5,000 msPeer event registration
PEER_EVENT_RETRY_WAIT_TIME500 msDelay between peer event retry attempts
PEER_EVENT_RECONNECTION_WARNING_RATE50Reconnection warning rate threshold
GENESISBLOCK_WAIT_TIME5,000 msGenesis block retrieval

For networks with stable connections and fast transaction processing, reduce PROPOSAL_WAIT_TIME and ORDERER_WAIT_TIME so the SDK switches to a healthy node more quickly.

Override via system property

Set system properties before the SDK initializes:

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

Override via config.properties

## Timeout for proposal requests to endorsers, in milliseconds.
org.hyperledger.fabric.sdk.proposal.wait.time = 5000
## Timeout for transactions sent to orderers, in milliseconds.
org.hyperledger.fabric.sdk.orderer.ordererWaitTimeMilliSecs = 5000

Increase the gRPC message size limit

The gRPC server rejects inbound messages larger than 4 MB (4,194,304 bytes) by default. When this limit is exceeded, the server throws:

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

To increase the limit, set grpc.NettyChannelBuilderOption.maxInboundMessageSize on the properties of each peer and orderer node. This configuration supports FABJ-480 in connection-profile.

NetworkConfig networkConfig = NetworkConfig.fromYamlFile(f);

// Apply the increased message size limit to all peer nodes
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);
}

// Apply the increased message size limit to all orderer nodes
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);
}

The example sets the limit to 100 MB (100 * 1024 * 1024 bytes). Adjust the value based on the maximum expected message size in your application.