Thrift常見異常及原因分析(UPDATING...


作者發現,本文被多個博客和網站轉發。贈人玫瑰,手有余香!
本文版權歸作者和博客園共有,歡迎轉載,轉載請注明出處: https://www.cnblogs.com/buguge

 

 【org.apache.thrift.TException家族】

 【Thrift架構】

以下是thrift的客戶端和服務端交互的一個原理圖。可以看到遵循了rpc框架的傳輸層、協議層和應用層三層。本文提到的異常就是與這三層相對應的傳輸異常TTransportException(ConnectException、SocketTimeoutException)、協議異常TProtocolException和應用異常TApplicationException。

 

■ org.apache.thrift.transport.TTransportException: java.net.SocketException: Connection reset

既然是Connection reset,即“連接被重置”,從字面意思就可以判斷出來,是連接的問題。那么,Thrift框架底層就是傳輸層,自然就是TTransport的問題了。什么問題呢?這個異常是由於client端指定的TTransport與服務端不一致導致的。demo中服務端是TFramedTransport,client端的TTransport實例是TSocket。

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

 

■ org.apache.thrift.transport.TTransportException: java.net.SocketTimeoutException: connect timed out

在執行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

 

■ org.apache.thrift.transport.TTransportException: java.net.ConnectException: Connection refused: connect

連接被拒絕

  • 服務端服務停止,客戶端無法建立soket連接,最終會出現這個TTransportException異常。
  • 我TSocket指定的是192.168.40.212的9898端口,在212上通過lsof -i:9898命令發現這個端口並沒有開放。也會報這個異常。

注意到這個異常是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

 

■ org.apache.thrift.transport.TTransportException: Cannot write to null outputstream

原因:客戶端未調用transport的open()方法,或者open失敗了,因socket輸出流是null而報TTransportException。一種情況是指定的遠程服務的地址(ip+端口)/節點名(zk負載情況下)或服務名壓根都不對,必然無法建立socket連接。

■ org.apache.thrift.transport.TTransportException: java.net.SocketTimeoutException: Read timed out

這個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

 

■ org.apache.thrift.transport.TTransportException

我在demo里如下2種調用rpc方法的情況報了這個異常。

  • 客戶端指向本機127.0.0.1的8080端口,在調用rpc方法時,報如下異常。因為本機Tomcat的8080端口雖然存在,但並未暴露所指定的thrift服務。
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)
  • 服務端協議層使用的傳輸格式是TMultiplexedProtocol,而client端調用時指定的是TCompactProtocol,在調用rpc方法時出現了異常。
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)

 

 

 

 


 

 

■ org.apache.thrift.TApplicationException: Internal error processing *方法名* 

當服務端出現未經捕獲的異常時,客戶端會收到這個異常。

這就要求thrift接口服務端一定要規避異常的拋出。

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)

 

■ org.apache.thrift.TApplicationException: *方法名* failed: unknown result

當服務端響應值為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)

■ org.apache.thrift.TApplicationException: Invalid method name: '*方法名*'

字面意思來理解是:客戶端調用的遠程方法,服務端並未暴露出來,導致這個異常。

實際上是什么情況呢?因為一個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);

又見如下這種情況:

thrift接口定義了方法,但是服務端實現類並未實現這個方法。 (我們現在的項目是thrfit接口單獨放在一個jar里,當給某個接口新加了方法后,server端沒有獲取最新jar是不會報錯的,而這時如果client端獲取最新jar了,那么在rpc調用時就會出現這個異常)
 
        
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)

 

 


■ org.apache.thrift.protocol.TProtocolException: Required field '***' was not present!

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

 

 

 ☞ Stay Hungry,Stay Foolish. 如果對閣下有幫助,就動動手指,點一下“推薦”喲~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM