使用HttpClient不設定超時將導致執行緒永久等待
阿新 • • 發佈:2019-01-11
最近在做一個定時任務,每一個小時使用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方法了