1. 程式人生 > >使用HttpClient不設定超時將導致執行緒永久等待

使用HttpClient不設定超時將導致執行緒永久等待

最近在做一個定時任務,每一個小時使用HttpClient去訪問一個外部伺服器拉去一組資料,使用一個大小為10的執行緒池啟動拉取執行緒,昨天外部伺服器掛了一次,然後發現我自己的伺服器上的執行緒池也掛了,提交的新task全部無法執行。奇怪的是外部伺服器昨天掛了,今天就恢復了,我自己的伺服器今天應該也自動恢復才對啊,我重啟了自己的伺服器的程序就OK了,但為什麼執行緒池會掛呢,即使外部服務恢復了,本地執行緒池必須要重啟來才能恢復,這是為何?

本地寫了一個小測試程式,自己搭建了一個簡單的http server,在server的服務方法中啟動死迴圈(或者直接打斷點不退出),就發現client端的請求執行緒一直卡住不會釋放,如果這個是執行緒池中的執行緒,就會一直佔用執行緒池資源,導致執行緒池不能響應後續的的任務。

解決辦法,設定socket超時時間:

SSLContextBuilder builder = new SSLContextBuilder();
            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    builder.build());

            RequestConfig requestConfig = RequestConfig.custom
().setSocketTimeout(5000) .setConnectTimeout(5000).setConnectionRequestTimeout(5000).build(); CloseableHttpClient client = HttpClientBuilder.create().setMaxConnTotal(3) .setMaxConnPerRoute(10000).setSSLSocketFactory(sslsf).build(); // URI uri = UriBuilder.fromPath
("http://localhost:8080/") URI uri = UriBuilder.fromPath("https://localhost:8443/") .segment("hello-world") .segment("get") .build(); HttpGet get = new HttpGet(uri); get.setConfig(requestConfig); System.out.println("http start"); try { CloseableHttpResponse response = client.execute(get); } catch (Exception e) { e.printStackTrace(); } System.out.println("http down"); return "ok";

這個時候執行緒就不會一直卡住了,達到超時時間就會會丟擲異常,並釋放執行緒資源:

java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:170)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
    at sun.security.ssl.InputRecord.read(InputRecord.java:503)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:930)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
    at org.apache.http.impl.conn.LoggingInputStream.read(LoggingInputStream.java:87)
    at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:136)
    at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:152)
    at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:270)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:140)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:260)
    at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:161)
    at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:153)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:271)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:254)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
    at dropwizardDemo.client.TestHttpClient$TestCallable.call(TestHttpClient.java:64)
    at dropwizardDemo.client.TestHttpClient$TestCallable.call(TestHttpClient.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

可見丟擲這個異常的方法已經是native方法了