thrift常見異常及緣由分析(updating...)

 【org.apache.thrift.TException家族】

 【Thrift架構】

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

 

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

既然是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

 

■ 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

鏈接被拒絕app

  • 服務端服務中止,客戶端沒法創建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鏈接。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方法的狀況報了這個異常。ui

  • 客戶端指向本機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 *方法名* 

當服務端出現未經捕獲的異常時,客戶端會收到這個異常。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)

 

■ 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);

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

相關文章
相關標籤/搜索