fabric-sdk-java for Service Discovery

前言

在fabric区块链网络下,fabric-sdk-java开发的application主要与peer节点交互,通过peer节点提交proposal request,完成背书之后提交到orderer节点进行排序打包。
在此过程中,若peer节点出现故障,就会导致proposal request提交失败,进而造成application不可用。因此,在fabric 1.2版本中提出了 Service Discovery来解决peer节点高可用的问题。

Service Discovery for fabric-sdk-java

以下demo使用fabric-sdk-java服务发现API实现查询和提交交易
此demo使用fabric-java-sd依赖版本为 1.4.8。

<!-- https://mvnrepository.com/artifact/org.hyperledger.fabric-sdk-java/fabric-sdk-java -->
    <dependency>
      <groupId>org.hyperledger.fabric-sdk-java</groupId>
      <artifactId>fabric-sdk-java</artifactId>
      <version>1.4.8</version>
    </dependency>

采用单机部署的raft共识的fabric区块链网络,5个orderer节点,4个peer节点,fabric版本为1.4.6
代码后面有相关的注意事项,有兴趣的朋友建议看到最后。
本demo采用的fabric网络实例地址:https://download.csdn.net/download/weixin_43562234/12442601

代码

TestServiceDiscovery.class

package com.demo.fabric.serviceDiscovery;

import com.demo.fabric.serviceDiscovery.configure.Configurations;
import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.security.CryptoSuite;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class TestServiceDiscovery {
    private static final long waitTime = 10000;


    public static void main(String[] args) throws Exception {
        //Configurations configurations = new Configurations();
   
        HFClient client = HFClient.createNewInstance();

        String userName = "Admin@org1.example.com";
        String mspId = "Org1MSP";
        String chaincodeName = "mycc";

        String keyDir = "src/main/java/com/demo/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore";
        String keyFile = getKeyFilesInDir(new File(keyDir)).toString();
        String certFile = "src/main/java/com/demo/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem";


        //create user object
        FabricUser user = new FabricUser(userName, mspId, keyFile, certFile);

        //encryption suite
        client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
        client.setUserContext(user);

        String channelName = "mychannel";

        //create channel object
        Channel channel = client.newChannel(channelName);

        //create peer0org1
        String peer0org1TLSCert = "src/main/java/com/demo/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt";
        Properties peer0org1_properties = new Properties();
        peer0org1_properties.put("pemBytes", Files.readAllBytes(Paths.get(peer0org1TLSCert)));
        peer0org1_properties.setProperty("sslProvider", "openSSL");
        peer0org1_properties.setProperty("negotiationType", "TLS");
        peer0org1_properties.setProperty("trustServerCertificate", "true");
        String peer0org1Name = "peer0.org1.example.com";
        String peer0org1URL = "grpcs://peer0.org1.example.com:7051";
        peer0org1_properties.setProperty("hostnameOverride", peer0org1Name);

        Peer peer0org1 = client.newPeer(peer0org1Name, peer0org1URL, peer0org1_properties);

        //create peer1org1
        String peer1org1TLSCert = "src/main/java/com/demo/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt";
        Properties peer1org1_properties = new Properties();
        peer1org1_properties.put("pemBytes", Files.readAllBytes(Paths.get(peer1org1TLSCert)));
        peer1org1_properties.setProperty("sslProvider", "openSSL");
        peer1org1_properties.setProperty("negotiationType", "TLS");
        peer1org1_properties.setProperty("trustServerCertificate", "true");
        String peer1org1Name = "peer1.org1.example.com";
        String peer1org1URL = "grpcs://peer1.org1.example.com:8051";
        peer1org1_properties.setProperty("hostnameOverride", peer1org1Name);

        Peer peer1org1 = client.newPeer(peer1org1Name, peer1org1URL, peer1org1_properties);

        //create peer0org2
        String peer0org2TLSCert = "src/main/java/com/demo/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt";
        Properties peer0org2_properties = new Properties();
        peer0org2_properties.put("pemBytes", Files.readAllBytes(Paths.get(peer0org2TLSCert)));
        peer0org2_properties.setProperty("sslProvider", "openSSL");
        peer0org2_properties.setProperty("negotiationType", "TLS");
        peer0org2_properties.setProperty("trustServerCertificate", "true");
        String peer0org2Name = "peer0.org2.example.com";
        String peer0org2URL = "grpcs://peer0.org2.example.com:9051";
        peer0org2_properties.setProperty("hostnameOverride", peer0org2Name);
        Peer peer0org2 = client.newPeer(peer0org2Name, peer0org2URL, peer0org2_properties);

        //create peer1org2
        String peer1org2TLSCert = "src/main/java/com/demo/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt";
        Properties peer1org2_properties = new Properties();
        peer1org2_properties.put("pemBytes", Files.readAllBytes(Paths.get(peer1org2TLSCert)));
        peer1org2_properties.setProperty("sslProvider", "openSSL");
        peer1org2_properties.setProperty("negotiationType", "TLS");
        peer1org2_properties.setProperty("trustServerCertificate", "true");
        String peer1org2Name = "peer1.org2.example.com";
        String peer1org2URL = "grpcs://peer1.org2.example.com:10051";
        peer1org2_properties.setProperty("hostnameOverride", peer1org2Name);
        Peer peer1org2 = client.newPeer(peer1org2Name, peer1org2URL, peer1org2_properties);


        channel.addPeer(peer0org1, Channel.PeerOptions.createPeerOptions().setPeerRoles(EnumSet.of(Peer.PeerRole.SERVICE_DISCOVERY, Peer.PeerRole.LEDGER_QUERY, Peer.PeerRole.EVENT_SOURCE, Peer.PeerRole.CHAINCODE_QUERY)));
        channel.addPeer(peer1org1, Channel.PeerOptions.createPeerOptions().setPeerRoles(EnumSet.of(Peer.PeerRole.SERVICE_DISCOVERY, Peer.PeerRole.LEDGER_QUERY, Peer.PeerRole.EVENT_SOURCE, Peer.PeerRole.CHAINCODE_QUERY)));
        channel.addPeer(peer0org2, Channel.PeerOptions.createPeerOptions().setPeerRoles(EnumSet.of(Peer.PeerRole.SERVICE_DISCOVERY, Peer.PeerRole.LEDGER_QUERY, Peer.PeerRole.EVENT_SOURCE, Peer.PeerRole.CHAINCODE_QUERY)));
        channel.addPeer(peer1org2, Channel.PeerOptions.createPeerOptions().setPeerRoles(EnumSet.of(Peer.PeerRole.SERVICE_DISCOVERY, Peer.PeerRole.LEDGER_QUERY, Peer.PeerRole.EVENT_SOURCE, Peer.PeerRole.CHAINCODE_QUERY)));



        //init channel
        channel.initialize();

        //queryByChaincode(client, chaincodeName, channel);
        insertByChaincode(client,chaincodeName,channel);
    }

    public static void queryByChaincode(HFClient client, String chaincodeName , Channel channel) throws Exception {
       
        ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).build();

        //build args
        ArrayList<String> argsList = new ArrayList<>();
        argsList.add("a");
        String function = "query";
        executeTransaction(client, channel, chaincodeID,false, function, argsList);
    }

    public static void insertByChaincode(HFClient client, String chaincodeName , Channel channel) throws Exception {
        ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).build();

        ArrayList<String> argsList = new ArrayList<>();
        argsList.add("b");
        argsList.add("a");
        argsList.add("10");

        String function = "invoke";
        executeTransaction(client, channel, chaincodeID,true ,function, argsList);

    }

    private static void executeTransaction(HFClient client, Channel channel, ChaincodeID chaincodeID, boolean invoke, String func, ArrayList<String> args) throws Exception {
        TransactionProposalRequest transactionProposalRequest = client.newTransactionProposalRequest();
        transactionProposalRequest.setChaincodeID(chaincodeID);
        transactionProposalRequest.setChaincodeLanguage(TransactionRequest.Type.JAVA);

        transactionProposalRequest.setFcn(func);
        transactionProposalRequest.setArgs(args);
        transactionProposalRequest.setProposalWaitTime(waitTime);
        List<ProposalResponse> successful = new LinkedList<ProposalResponse>();
        List<ProposalResponse> failed = new LinkedList<ProposalResponse>();
        // java sdk
        // 通常会发送交易请求给所有peer节点,如果有一些peer节点宕机,但是有Response到达了背书节点的话,我们可以不选择重新发送交易请求,使用服务发现功能可以实现这一目标。
        Collection<ProposalResponse> transactionPropResp;
        // 确保配置有服务发现功能的peer节点数量大于0
        if (channel.getPeers(EnumSet.of(Peer.PeerRole.SERVICE_DISCOVERY)).size() > 0) {
            System.out.println("配置有服务发现功能的peer节点数量:" + channel.getPeers(EnumSet.of(Peer.PeerRole.SERVICE_DISCOVERY)).size());
            // 配置服务发现在fabric网络中,使用服务发现来寻找背书节点(endorsing peers)
            Channel.DiscoveryOptions discoveryOptions = Channel.DiscoveryOptions.createDiscoveryOptions();
            // 随机选取满足背书策略的peer节点组合 ENDORSEMENT_SELECTION_RANDOM
            // 选取满足背书策略的,状态最新、块高最大的peer节点组合 ENDORSEMENT_SELECTION_LEAST_REQUIRED_BLOCKHEIGHT
            discoveryOptions.setEndorsementSelector(ServiceDiscovery.EndorsementSelector.ENDORSEMENT_SELECTION_RANDOM);
            // discoveryOptions.setEndorsementSelector(ServiceDiscovery.EndorsementSelector.ENDORSEMENT_SELECTION_LEAST_REQUIRED_BLOCKHEIGHT);
            // setForceDiscovery true :每一次发送proposal时都调用discovery服务获取peer列表,会有一定的资源消耗
            // setForceDiscovery false :发送proposal时使用discovery服务缓存的peer列表,默认2分钟刷新一次
            discoveryOptions.setForceDiscovery(false);
            // setInspectResults true: 关闭SDK 背书策略检查,由应用逻辑进行判断
            // false:SDK 自动进行背书策略检查,不满足抛出异常
            discoveryOptions.setInspectResults(true);
            transactionPropResp = channel.sendTransactionProposalToEndorsers(transactionProposalRequest, discoveryOptions);
        } else {
            System.out.println("peers:" + channel.getPeers(EnumSet.of(Peer.PeerRole.SERVICE_DISCOVERY)).size());
            transactionPropResp = channel.sendTransactionProposal(transactionProposalRequest, channel.getPeers(EnumSet.of(Peer.PeerRole.ENDORSING_PEER)));
        }

        for (ProposalResponse response : transactionPropResp) {
            System.out.println("ChaincodeActionResponseStatus:"+response.getChaincodeActionResponseStatus());
            System.out.println("ChaincodeActionResponsePayload:"+ Arrays.toString(response.getChaincodeActionResponsePayload()));
            System.out.println(""+response.getProposalResponse());
            //System.out.println();
            if (response.getStatus() == ProposalResponse.Status.SUCCESS) {
                String payload = response.getProposalResponse().getResponse().getPayload().toStringUtf8();
                if (payload.isEmpty()) {
                    System.out.println("******************************************************************************");
                    System.out.println(String.format("[√] 得到成功响应从 peer %s", response.getPeer().getName()));
                } else {
                    System.out.println("******************************************************************************");
                    System.out.println(
                            String.format("[√] 得到成功响应从 peer %s => payload: %s", response.getPeer().getName(), payload));
                }
                successful.add(response);
            } else {
                String status = response.getStatus().toString();
                String msg = response.getMessage();
                System.out.println(String.format("[×] 响应失败从 peer %s => %s: %s ", response.getPeer().getName(), status, msg));
                failed.add(response);
            }
        }

        if (invoke) {
            Channel.TransactionOptions opts = new Channel.TransactionOptions();
            Channel.NOfEvents nOfEvents = Channel.NOfEvents.createNofEvents();
            nOfEvents.addPeers(channel.getPeers(EnumSet.of(Peer.PeerRole.EVENT_SOURCE)));
            nOfEvents.setN(1);
            opts.nOfEvents(nOfEvents);
            System.out.println("向orderers发送交易...");
            channel.sendTransaction(successful, opts).thenApply(transactionEvent -> {
                System.out.println("Orderer 响应: txid" + transactionEvent.getTransactionID());
                System.out.println("Orderer 响应: 区块编号: " + transactionEvent.getBlockEvent().getBlockNumber());
                return null;
            }).exceptionally(e -> {
                System.out.println("Orderer exception happened: "+ e);
                return null;
            }).get(waitTime, TimeUnit.SECONDS);
        }

    }

    private static File getKeyFilesInDir(File filePath) {
        File keyFile = null;
        File[] listFiles = filePath.listFiles();
        if (listFiles != null) {
            for (File file : listFiles) {
                if (file.isFile()) {
                    if (file.getName().endsWith("_sk")) {
                        keyFile = file;
                        break;
                    }
                }
            }
        }
        return keyFile;
    }

}

FabricUser.class

package com.demo.fabric.serviceDiscovery;

import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.Set;


/**
 * @author adder
 * @date 2020/1/20 15:43
 */
public class FabricUser implements User {
    private String name;
    private String mspId;
    private Enrollment enrollment;
    private String keyFile;
    private String certFile;

    public FabricUser(String name, String mspId, String keyFile, String certFile) {
        this.name = name;
        this.mspId = mspId;
        this.keyFile=keyFile;
        this.certFile=certFile;

        try{
            enrollment=loadFromPemFile(keyFile, certFile);
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

    private Enrollment loadFromPemFile(String keyFile,String certFile) throws Exception{
        byte[] keyPem = Files.readAllBytes(Paths.get(keyFile));     //load private key text
        byte[] certPem = Files.readAllBytes(Paths.get(certFile));   //load certificate text
        CryptoPrimitives suite = new CryptoPrimitives();            //load the cryptography suite
        PrivateKey privateKey = suite.bytesToPrivateKey(keyPem);    //convert private key text to object
        return new X509Enrollment(privateKey,new String(certPem));  //create X509Enrollment object
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getMspId() {
        return mspId;
    }

    @Override
    public Enrollment getEnrollment() {
        return enrollment;
    }

    @Override
    public String getAccount() {
        return null;
    }

    @Override
    public String getAffiliation() {
        return null;
    }

    @Override
    public Set<String> getRoles() {
        return null;
    }

    public String getKeyFile() {
        return keyFile;
    }

    public void setKeyFile(String keyFile) {
        this.keyFile = keyFile;
    }

    public String getCertFile() {
        return certFile;
    }

    public void setCertFile(String certFile) {
        this.certFile = certFile;
    }

}

注意!!!

  1. 服务发现主要依赖于GOSSIP协议,通过在peer节点的配置文件中添加CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:8051配置,来实现peer节点主动向channel中广播自己的状态。
  2. orderer节点本身会向channel广播自身的状态,因此不需要修改orderer节点的配置文件,在开发application时调用java sdk的服务发现的API也不需要向channel中add orderer节点(没有使用服务发现的时候是需要在channel中添加orderer的)。
  3. orderer节点通过grpc服务与外部通讯,其grpc服务端口默认的是7050。需要注意的是,在单台服务器上部署多个orderer节点的时候,需要修改ORDERER_GENERAL_LISTENPORT=7050环境变量的端口号,使其与orderer节点在服务器上对外暴露的端口保持一致,否则在使用sdk调用服务发现API的时候会出现异常,异常信息如下:
19:10:04.242 [main] DEBUG org.hyperledger.fabric.sdk.Orderer - Orderer.sendTransaction Orderer{id: 18, channelName: mychannel, name:orderer4.example.com:7050, url: grpcs://orderer4.example.com:7050}
19:10:04.244 [main] DEBUG org.hyperledger.fabric.sdk.Endpoint - Endpoint grpcs://orderer4.example.com:7050 with no ssl context
19:10:04.254 [grpc-default-worker-ELG-1-16] DEBUG io.netty.handler.ssl.ReferenceCountedOpenSslContext - verification of certificate failed
java.security.cert.CertificateException: No subject alternative DNS name matching orderer4.example.com found.
	at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:214)
	at sun.security.util.HostnameChecker.match(HostnameChecker.java:96)
	at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455)
	at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:436)
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:252)
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:136)
	at io.netty.handler.ssl.OpenSslTlsv13X509ExtendedTrustManager.checkServerTrusted(OpenSslTlsv13X509ExtendedTrustManager.java:223)
	at io.netty.handler.ssl.ReferenceCountedOpenSslClientContext$ExtendedTrustManagerVerifyCallback.verify(ReferenceCountedOpenSslClientContext.java:255)
	at io.netty.handler.ssl.ReferenceCountedOpenSslContext$AbstractCertificateVerifier.verify(ReferenceCountedOpenSslContext.java:701)
	at io.netty.internal.tcnative.SSL.readFromSSL(Native Method)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.readPlaintextData(ReferenceCountedOpenSslEngine.java:593)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1176)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1293)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1336)
	at io.netty.handler.ssl.SslHandler$SslEngineType$1.unwrap(SslHandler.java:204)
	at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1332)
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1227)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1274)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:503)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:442)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:281)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:700)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
	at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1050)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)
19:10:04.259 [grpc-default-executor-1] ERROR org.hyperledger.fabric.sdk.OrdererClient - OrdererClient{id: 23, channel: mychannel, name: orderer4.example.com:7050, url: grpcs://orderer4.example.com:7050}  managed channel isTerminated: false, isShutdown: false, state: TRANSIENT_FAILURE
19:10:04.259 [grpc-default-executor-1] ERROR org.hyperledger.fabric.sdk.OrdererClient - Received error org.hyperledger.fabric.sdk.OrdererClient$1@6e8ba5db  UNAVAILABLE: io exception
io.grpc.StatusRuntimeException: UNAVAILABLE: io exception
	at io.grpc.Status.asRuntimeException(Status.java:530)
	at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onClose(ClientCalls.java:434)
	at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
	at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
	at io.grpc.internal.CensusStatsModule$StatsClientInterceptor$1$1.onClose(CensusStatsModule.java:694)
	at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
	at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
	at io.grpc.internal.CensusTracingModule$TracingClientInterceptor$1$1.onClose(CensusTracingModule.java:397)
	at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:459)
	at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:63)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:546)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$600(ClientCallImpl.java:467)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:584)
	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: javax.net.ssl.SSLHandshakeException: General OpenSslEngine problem
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.handshakeException(ReferenceCountedOpenSslEngine.java:1732)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.wrap(ReferenceCountedOpenSslEngine.java:774)
	at javax.net.ssl.SSLEngine.wrap(SSLEngine.java:509)
	at io.netty.handler.ssl.SslHandler.wrap(SslHandler.java:1046)
	at io.netty.handler.ssl.SslHandler.wrapNonAppData(SslHandler.java:937)
	at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1395)
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1227)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1274)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:503)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:442)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:281)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:700)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
	at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1050)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	... 1 common frames omitted

这个异常是因为我的orderer节点在虚拟机上对外开放的端口是10050,但是服务发现在channel中获取到的是7050,因此sdk在使用我们提供的orderer节点的endpoint与orderer通讯的时候会失败,但是交易仍然会成功,原因是服务发现会将开发者手动加入到通道中的orderer节点与其查询出来的orderer节点的信息进行对比,如果两者不同,那么手动加入的orderer节点信息会被删除掉,采用服务发现自动获取的orderer节点。源码如图:
在这里插入图片描述
结合fabric网络分析:orderer节点在channel中广播的endpoint则是域名:LISTENPORT,那么service discovery在channel中查询到的orderer节点的endpoint就是域名:7050,因此在使用sdk开发application的时候需要注意一下。

如果demo测试过程中出现了问题,欢迎在本博客下留言,笔者会及时回复

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐