在fabric区块链网络下,fabric-sdk-java开发的application主要与peer节点交互,通过peer节点提交proposal request,完成背书之后提交到orderer节点进行排序打包。
在此过程中,若peer节点出现故障,就会导致proposal request提交失败,进而造成application不可用。因此,在fabric 1.2版本中提出了 Service Discovery来解决peer节点高可用的问题。
以下demo使用fabric-sdk-java服务发现API实现查询和提交交易
此demo使用fabric-java-sd依赖版本为 1.4.8。
org.hyperledger.fabric-sdk-java
fabric-sdk-java
1.4.8
采用单机部署的raft共识的fabric区块链网络,5个orderer节点,4个peer节点,fabric版本为1.4.6
代码后面有相关的注意事项,有兴趣的朋友建议看到最后。
本demo采用的fabric网络实例地址:https://download.csdn.net/download/weixin_43562234/12442601
TestServiceDiscovery.class
package com.richfit.fabric.serviceDiscovery;
import com.richfit.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 = "[email protected]";
String mspId = "Org1MSP";
String chaincodeName = "mycc";
String keyDir = "src/main/java/com/richfit/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/keystore";
String keyFile = getKeyFilesInDir(new File(keyDir)).toString();
String certFile = "src/main/java/com/richfit/fabric/serviceDiscovery/configure/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/signcerts/[email protected]";
//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/richfit/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/richfit/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/richfit/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/richfit/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 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 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 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 successful = new LinkedList();
List failed = new LinkedList();
// java sdk
// 通常会发送交易请求给所有peer节点,如果有一些peer节点宕机,但是有Response到达了背书节点的话,我们可以不选择重新发送交易请求,使用服务发现功能可以实现这一目标。
Collection 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.richfit.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 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;
}
}
CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:8051
配置,来实现peer节点主动向channel中广播自己的状态。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的时候需要注意一下。