如下是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服務端可能停了