你的httpclient真的使用正確嗎

我用java開發(fā)類似scrapy的工具包時,在使用httpclient做網(wǎng)絡(luò)請求,遇到了請求無限卡死的問題,今天將其解決方案拿出來,避免后人踩坑。

問題如下:
RequestConfig.custom().setSocketTimeout(SO_TIME_OUT).setConnectTimeout(CONNECTION_TIME_OUT).setConnectionRequestTimeout(CONNECTION_REQUEST_TIME_OUT)

在設(shè)置了常規(guī)的超時配置如socketTimeout、connectTimeout和connectionRequestTimeout,在大并發(fā)情況下,時不時會出現(xiàn)部分請求在java.net.SocketInputStream.socketRead0方法中一直卡死,dump出的信息如下:

"pool-2-thread-87" #202 prio=5 os_prio=0 tid=0x00007f52603a8000 nid=0x6672 runnable [0x00007f51888c6000]
   java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:171)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
    at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
    at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:282)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
    at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
    at org.apache.http.impl.execchain.MainClientExec.createTunnelToTarget(MainClientExec.java:486)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:411)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
排查自己是否踩坑

可以通過運行命令:jstack -l ${pid} | grep java.net.SocketInputStream.socketRead0
如果多次dump信息都有同一個線程棧每次都出現(xiàn),則可以判斷你的服務(wù)也存在這種問題

解決方案
  1. 如果是https的,并且使用的是4.3.5版本的httpclient版本,升級httpclient版本即可,這個是httpclient的bug,詳情見https://issues.apache.org/jira/browse/HTTPCLIENT-1589,這是第一種可能性的解決方案。
  2. 如果第一種不能解決,基本都是這種可能性了。我在使用httpclient的代理請求,經(jīng)過無數(shù)次的debug,發(fā)現(xiàn)代理請求時在TUNNEL_TARGET步驟,里面的連接用的DefaultBHttpClientConnection綁定的socket,使用的是SocketConfig配置,而不是RequestConfig,所以在沒有設(shè)置SocketConfig情況下,socket的ocketRead0方法是無限等待的,就會造成線程一直卡死。增加下面的配置即可:
connectionManager.setSocketConfig(SocketConfig.custom().setSoTimeout(SO_TIME_OUT).build());

另外,凡是用到socket的,可能都需要注意是否設(shè)置了socket的timeout,不然就會出現(xiàn)一直socketRead0的情況。

本文作者:二當(dāng)家的
同名博客文章鏈接: 2018/12/15/你的httpclient真的使用正確嗎
本博客所有文章除特別聲明外,均采用 CC BY-NC-SA 3.0 CN 許可協(xié)議。轉(zhuǎn)載請注明出處!

二當(dāng)家的黑板報

掃描上面微信公眾號,獲取更多技術(shù)信息

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容