如下是thrift的客户端和服务端交互的一个原理图。能够看到遵循了rpc框架的传输层、协议层和应用层三层。本文提到的异常就是与这三层相对应的传输异常TTransportException(ConnectException、SocketTimeoutException)、协议异常TProtocolException和应用异常TApplicationException。java
既然是Connection reset,即“链接被重置”,从字面意思就能够判断出来,是链接的问题。那么,Thrift框架底层就是传输层,天然就是TTransport的问题了。什么问题呢?这个异常是因为client端指定的TTransport与服务端不一致致使的。demo中服务端是TFramedTransport,client端的TTransport实例是TSocket。apache
org.apache.thrift.transport.TTransportException: java.net.SocketException: Connection reset at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:129) at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86) at org.apache.thrift.protocol.TBinaryProtocol.readAll(TBinaryProtocol.java:425) at org.apache.thrift.protocol.TBinaryProtocol.readI32(TBinaryProtocol.java:321) at org.apache.thrift.protocol.TBinaryProtocol.readMessageBegin(TBinaryProtocol.java:225) at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:77) at com.emaxcard.route.thrift.quickpay.TBatchPayQueryService$Client.recv_batchPayQuery(TBatchPayQueryService.java:61) at com.emaxcard.route.thrift.quickpay.TBatchPayQueryService$Client.batchPayQuery(TBatchPayQueryService.java:48) at com.emaxcard.ThriftTest.main(ThriftTest.java:38) Caused by: java.net.SocketException: Connection reset at java.net.SocketInputStream.read(SocketInputStream.java:209) at java.net.SocketInputStream.read(SocketInputStream.java:141) at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) at java.io.BufferedInputStream.read1(BufferedInputStream.java:286) at java.io.BufferedInputStream.read(BufferedInputStream.java:345) at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:127) ... 8 more
在执行transport.open()与服务端创建链接时,超时了。服务端响应时间超出了客户端设置的connectTimeout值。BTW,由于thrift多应用于局域网分布式系统,因此一般状况下不会出现链接超时,多是所指定的服务压根儿就不存在(需检查IP和端口是否正确)架构
org.apache.thrift.transport.TTransportException: java.net.SocketTimeoutException: connect timed out at org.apache.thrift.transport.TSocket.open(TSocket.java:226) at com.emaxcard.ThriftTest.main(ThriftTest.java:30) Caused by: java.net.SocketTimeoutException: connect timed out at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at org.apache.thrift.transport.TSocket.open(TSocket.java:221) ... 1 more
链接被拒绝app
注意到这个异常是java.net.ConnectException的Connection refused: connect。当咱们发起一个http请求,当http接口的服务端是如上两种状况时,也会出现这个异常。框架
org.apache.thrift.transport.TTransportException: java.net.ConnectException: Connection refused: connect at org.apache.thrift.transport.TSocket.open(TSocket.java:226) at HelloServiceClient.main(HelloServiceClient.java:26) Caused by: java.net.ConnectException: Connection refused: connect at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:579) at org.apache.thrift.transport.TSocket.open(TSocket.java:221) ... 1 more
缘由:客户端未调用transport的open()方法,或者open失败了,因socket输出流是null而报TTransportException。一种状况是指定的远程服务的地址(ip+端口)/节点名(zk负载状况下)或服务名压根都不对,必然没法创建socket链接。socket
这个socketTimeout异常就很容易理解了。客户端设置了socketTimeout,而服务端方法未能在这个时间内响应。分布式
TTransport transport = new TSocket("localhost", 9898, socketTimeout, connectTimeout);
示例中我设置socketTimeout=2000,让服务端方法线程sleep3秒,结果就会出现这个异常。监测客户端调用的duration=2027,大于设定的2000。
org.apache.thrift.transport.TTransportException: java.net.SocketTimeoutException: Read timed out at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:129) at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86) at org.apache.thrift.protocol.TBinaryProtocol.readAll(TBinaryProtocol.java:425) at org.apache.thrift.protocol.TBinaryProtocol.readI32(TBinaryProtocol.java:321) at org.apache.thrift.protocol.TBinaryProtocol.readMessageBegin(TBinaryProtocol.java:225) at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:77) at com.zhanggz.test.rpc.service.AgentPayService$Client.recv_apply(AgentPayService.java:61) at com.zhanggz.test.rpc.service.AgentPayService$Client.apply(AgentPayService.java:48) at HelloServiceClient.main(HelloServiceClient.java:36) Caused by: java.net.SocketTimeoutException: Read timed out at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.read(SocketInputStream.java:152) at java.net.SocketInputStream.read(SocketInputStream.java:122) at java.io.BufferedInputStream.fill(BufferedInputStream.java:235) at java.io.BufferedInputStream.read1(BufferedInputStream.java:275) at java.io.BufferedInputStream.read(BufferedInputStream.java:334) at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:127) ... 8 more
我在demo里以下2种调用rpc方法的状况报了这个异常。ui
org.apache.thrift.transport.TTransportException at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:132) at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86) at org.apache.thrift.protocol.TBinaryProtocol.readStringBody(TBinaryProtocol.java:379) at org.apache.thrift.protocol.TBinaryProtocol.readMessageBegin(TBinaryProtocol.java:236) at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:77) at com.zhanggz.test.rpc.service.AgentPayService$Client.recv_apply(AgentPayService.java:61) at com.zhanggz.test.rpc.service.AgentPayService$Client.apply(AgentPayService.java:48) at HelloServiceClient.main(HelloServiceClient.java:36)
org.apache.thrift.transport.TTransportException at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:132) at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86) at org.apache.thrift.transport.TFramedTransport.readFrame(TFramedTransport.java:132) at org.apache.thrift.transport.TFramedTransport.read(TFramedTransport.java:100) at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86) at org.apache.thrift.protocol.TCompactProtocol.readByte(TCompactProtocol.java:637) at org.apache.thrift.protocol.TCompactProtocol.readMessageBegin(TCompactProtocol.java:505) at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:77) at com.emaxcard.route.thrift.TBatchPayQueryService$Client.recv_batchPayQuery(TBatchPayQueryService.java:61) at com.emaxcard.route.thrift.TBatchPayQueryService$Client.batchPayQuery(TBatchPayQueryService.java:48) at com.emaxcard.ThriftTest.main(ThriftTest.java:43)
当服务端出现未经捕获的异常时,客户端会收到这个异常。spa
这就要求thrift接口服务端必定要规避异常的抛出。.net
org.apache.thrift.TApplicationException: Internal error processing apply at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:79) at com.zhanggz.test.rpc.service.AgentPayService$Client.recv_apply(AgentPayService.java:61) at com.zhanggz.test.rpc.service.AgentPayService$Client.apply(AgentPayService.java:48) at HelloServiceClient.main(HelloServiceClient.java:35)
当服务端响应值为null时,客户端会收到这个异常。其中,TApplicationException是TException的一个派生类。
这就要求thrift接口服务端是不容许返回null的。
org.apache.thrift.TApplicationException: apply failed: unknown result at com.zhanggz.test.rpc.service.AgentPayService$Client.recv_apply(AgentPayService.java:65) at com.zhanggz.test.rpc.service.AgentPayService$Client.apply(AgentPayService.java:48) at HelloServiceClient.main(HelloServiceClient.java:35)
字面意思来理解是:客户端调用的远程方法,服务端并未暴露出来,致使这个异常。
其实是什么状况呢?由于一个interface的方法默认都是public的,因此并不存在一个interface的某个方法不能被访问。之因此抛出这个异常,其实是客户端所调用的thrift接口.Client实例,服务端并未暴露thrift接口.Processor。
见以下这种状况:
服务端暴露的接口(Processor):TProcessor tprocessor = new AgentPayService.Processor<AgentPayService.Iface>(new AgentPayServiceImpl());
---么么哒(incaseof 服务端提供的thrift接口jar包里有AgentPayService和HelloService)---
客户端调用的接口(Client): HelloService.Client client = new HelloService.Client(protocol);
org.apache.thrift.TApplicationException: Invalid method name: 'helloString' at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:79) at com.zhanggz.test.rpc.service.HelloService$Client.recv_helloString(HelloService.java:61) at com.zhanggz.test.rpc.service.HelloService$Client.helloString(HelloService.java:48) at HelloServiceClient.main(HelloServiceClient.java:36)
thrift定义了参数为required。而程序在请求或返回时未对其赋值,会出现这个异常。
struct BatchPayQueryResponseVO{ /**返回码*/ 1: required i32 responseCode; ~~~ ~~~ /**上游渠道paymentId*/ 9:required string channelPaymentId;
org.apache.thrift.protocol.TProtocolException: Required field 'channelPaymentId' was not present! Struct: BatchPayQueryResponseVO(responseCode:1002, responseMsg:渠道处理失败, payStatus:null, payStatusText:null, paymentId:32300, amount:0, fee:0, bankSerTime:null, channelPaymentId:null, channelBatchId:null, pyerBankSerialNo:null, pyeeBankSerialNo:null) at com.emaxcard.route.thrift.quickpay.BatchPayQueryResponseVO.validate(BatchPayQueryResponseVO.java:1292) ~[gateway_thrift-1.0-SNAPSHOT.jar:?] at com.emaxcard.route.thrift.quickpay.TBatchPayQueryService$batchPayQuery_result.validate(TBatchPayQueryService.java:856) ~[gateway_thrift-1.0-SNAPSHOT.jar:?] at com.emaxcard.route.thrift.quickpay.TBatchPayQueryService$batchPayQuery_result$batchPayQuery_resultStandardScheme.write(TBatchPayQueryService.java:915) ~[gateway_thrift-1.0-SNAPSHOT.jar:?] at com.emaxcard.route.thrift.quickpay.TBatchPayQueryService$batchPayQuery_result$batchPayQuery_resultStandardScheme.write(TBatchPayQueryService.java:882) ~[gateway_thrift-1.0-SNAPSHOT.jar:?] at com.emaxcard.route.thrift.quickpay.TBatchPayQueryService$batchPayQuery_result.write(TBatchPayQueryService.java:833) ~[gateway_thrift-1.0-SNAPSHOT.jar:?] at org.apache.thrift.ProcessFunction.process(ProcessFunction.java:57) ~[libthrift-0.11.0.jar:0.11.0] at org.apache.thrift.TBaseProcessor.process(TBaseProcessor.java:39) ~[libthrift-0.11.0.jar:0.11.0] at org.apache.thrift.TMultiplexedProcessor.process(TMultiplexedProcessor.java:134) ~[libthrift-0.11.0.jar:0.11.0] at org.apache.thrift.server.AbstractNonblockingServer$FrameBuffer.invoke(AbstractNonblockingServer.java:518) [libthrift-0.11.0.jar:0.11.0] at org.apache.thrift.server.Invocation.run(Invocation.java:18) [libthrift-0.11.0.jar:0.11.0] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_181] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_181] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_181]
■ NPE
缘由:thrift服务端可能停了