Apache HttpClient 沒有設定time out導致應用長時間阻塞的問題

橫雲斷嶺發表於2014-04-21

update 2014-5-18:

今天又處理了一個HttpClient阻塞的問題,還是socket read沒有超時設定。

用jstack -l pid 得到執行緒的呼叫棧,每隔一段時間執行一次,對比三次的thread dump結果,發現有一個執行緒是三次執行的位置一樣的,說明它是阻塞在這裡了:

"org.springframework.jms.listener.DefaultMessageListenerContainer#7-1" prio=10 tid=0x00007f345127d800 nid=0x5b4f0 runnable [0x00007f34753d1000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:150)
	at java.net.SocketInputStream.read(SocketInputStream.java:121)
	at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:130)
	at org.apache.http.impl.io.SocketInputBuffer.fillBuffer(SocketInputBuffer.java:127)
	at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:233)
	at org.apache.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:220)
	at org.apache.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:183)
	at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:152)
	at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:138)
	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
	- locked <0x000000070346ce70> (a java.io.InputStreamReader)
	at java.io.InputStreamReader.read(InputStreamReader.java:184)
	at java.io.Reader.read(Reader.java:140)
	at org.apache.http.util.EntityUtils.toString(EntityUtils.java:161)

業務的邏輯是接收一個JMS訊息,再http請求呼叫得到處理結果。在http請求處阻塞了,導致後面的訊息都不能訊息。

----------------------------------------------

現在的對外介面一般都是Http + json的,因為簡單,語言無關。

Apache HttpClient應該是最常用的Java http元件了。這貨有個坑爹的地方,Apache HttpClient如果對方不迴應,或者網路原因不返回了,那麼HttpClient會一直阻塞。這種情況在公網可能比較容易碰到。在內網時,也有一次因為一臺中轉的nginx掛掉而導致hessian請求長時間阻塞。

因為Http Client預設的SO_TIMEOUT是0,即一直等待。

這個問題,在幫同事查詢問題時碰到好幾次了,可能是大家潛意識裡認為Http請求是即時的,失敗的話也很快返回。


Apache HttpClient的示例也沒提到要設定TimeOut,這也是比較坑爹的地方。一個庫如果沒有預設阻止使用者去範錯誤,那麼你也應當在文件,示例程式碼裡提醒使用者不要範錯誤。

有三個可以設定time out 的引數:

httpClient = new DefaultHttpClient();
httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_LINGER, value)
httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 3000);
httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 3000);

SO_LINGER最好不要設定,可能會坑死人。參考:

http://unliminet.blog.51cto.com/380895/346686

http://stackoverflow.com/questions/3757289/tcp-option-so-linger-zero-when-its-required


相關文章