使用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方法了

你可能感兴趣的:(网络编程)