【HttpClient】connectionRequestTimeout 、connectionTimeout、socketTimeout含义

为什么要设置HTTP timeout?

  1. 与用户操作相关的接口,如果不设置超时时间,将会出现长时间的无响应,严重影响用户体验。
  2. 负载很高的系统,因为大量调用耗时长的接口,导致性能急剧下降,从而影响其他正常的业务。
  3. 某些情况下,HTTP请求可能永远都得不到响应,那么这部分系统资源就一直被占用,直到系统奔溃。

HttpClient超时配置参数的问题

客户端httpclient的关于连接的配置相关的参数和意义如下:

SocketTimeout 是 5s

  • 连接建立后,数据传输过程中数据包之间间隔的最大时间

ConnectTimeout 是 3s

  • 连接建立时间,即三次握手完成时间

ConnectionRequestTimeout 是默认值

  • httpclient使用连接池来管理连接,这个时间就是从连接池获取连接的超时时间

响应超时时间异常

在网络不好的时候, 请求超时时间并没有按照我们的设置报超时异常,明明响应超时时间设置了5s,很多请求超时时间都达到了10几秒甚至有的二十几秒,大大超过了我们的预期时间
在这里插入图片描述

虽然报文(“abc”)返回总共用了6秒,如果SocketTimeout设置成5秒,实际程序执行的时候是不会抛出java.net.SocketTimeoutException: Read timed out异常的。

  • 因为SocketTimeout的值表示的是“a”、”b”、”c”这三个报文,每两个相邻的报文的间隔时间没有能超过SocketTimeout

wireshark抓包结果
可能会想实际的报文是不是每经过3秒返回一个报文呢,下面是通过wireshark抓包的结果

1:返回字母a的TCP报文
2:返回字母b的TCP报文
3:返回字母c的TCP报文

根据time列可以看出TCP数据传输报文之间的间隔就是3秒。,每个报文后还有一个ACK的报文,就是tcp客户端的回执报文
在这里插入图片描述

  • 因此可以得出结论: SocketTimeout时间是我们两次读取数据之间的最长阻塞时间,如果我在网络抖动的情况下,我每次2秒之内返回一部分数据,这样我就一直不会超时了

Apache的Httpclient不同版本设置超时时间的方法是不一样的,要特别注意

3.x.超时设置实例

/* 从连接池中取连接的超时时间(单位是ms),超时则抛出ConnectionPoolTimeoutException*/ 
ConnManagerParams.setTimeout(params, 1000); 
/*客户端和服务器建立连接超时时间(单位是ms),超时则抛出ConnectionTimeOutException*/ 
HttpConnectionParams.setConnectionTimeout(params, 2000); 
/*(已建立连接)指客户端从服务器读取数据的超时时间(单位是ms),超时则抛出SocketTimeOutException*/
HttpConnectionParams.setSoTimeout(params, 4000);

4.x.超时设置实例

connectionRequestTimout(单位是ms):指从连接池获取连接的timeout超出预设时间(

  • 从连接池获取连接的超时时间,如果连接池里连接都被用了,且超过设定时间就会抛出ConnectionPoolTimeoutException

connetionTimeout(单位是ms):指客户端和服务器建立连接的timeout.

  • 就是http请求的三个阶段,一:建立连接;二:数据传送;三,断开连接。如果与服务器(这里指数据库)请求建立连接的时间超过ConnectionTimeOut,就会抛 ConnectionTimeOutException,即服务器连接超时,没有在规定的时间内建立连接。

socketTimeout(单位是ms)指客户端和服务进行数据交互的时间,是指两者之间如果两个数据包之间的时间大于该时间则认为超时,而不是整个交互的整体时间比如如果设置1秒超时,如果每隔0.8秒传输一次数据,传输10次,总共8秒,这样是不超时的。而如果任意两个数据包之间的时间超过了1秒,则超时。 超出后会抛出SocketTimeOutException.

public static void main(String[] args) throws Exception{
    
    
         
        //创建httpclient
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建http get
        HttpGet httpGet = new HttpGet("http://www.taotao.com/");
        //构建超时等配置信息
        RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) //连接超时时间
                .setConnectionRequestTimeout(1000) //从连接池中取的连接的最长时间
                .setSocketTimeout(10 *1000) //数据传输的超时时间
                .setStaleConnectionCheckEnabled(true) //提交请求前测试连接是否可用
                .build();
        //设置请求配置时间
        httpGet.setConfig(config);
         
        //接受返回的数据
        CloseableHttpResponse response = null;
         
        try {
    
    
            response = httpClient.execute(httpGet);
        }finally{
    
    
            if(response!=null){
    
    
                response.close();
            }
            httpClient.close();
        }
}

结合代码测试

注:下面面Case是使用httpclient版本4.5.2测试所得

		<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
            <type>jar</type>
        </dependency>

1.完整测试接口代码

/**
 * 超时测试接口
 */
@RestController
@RequestMapping("/test")
@Slf4j
public class TimeoutTestController {
    
    

    /**
     * 1.测试socketOutTimeout,三秒后返回数据
     *
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/socket_timeout")
    String socketTimeout() throws InterruptedException {
    
    
        log.info("socket_timeout");
        TimeUnit.MILLISECONDS.sleep(3000);
        return "/socket_timeout接口调用成功";
    }

    /**
     * 2.测试socketOutTimeout,
     * 每隔0.8秒返回数据,循环发送,模拟网络不好的时候,收到的数据断断续续
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/socket_timeout_2")
    void socketTimeout2(HttpServletResponse response) throws InterruptedException, IOException {
    
    
        log.info("socket_timeout_2");
        for (int i = 0; i < 10; i++) {
    
    
            log.info("{}", i);
            response.getWriter().println("" + i);
            response.flushBuffer();
            TimeUnit.MILLISECONDS.sleep(800);
        }
    }

    /**
     * 3.测试connectionRequestTimeout用的服务,三秒后返回数据
     *
     * @param request
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/connection_request_timeout")
    String connectionRequestTimeout(HttpServletRequest request) throws InterruptedException {
    
    
        log.info("{}", request.getRequestURI());
        TimeUnit.MILLISECONDS.sleep(3000);
        return "/connection_request_timeout调用成功";
    }
}

2.完整单元测试代码

/**
 * 测试HttpClient超时参数
 */
public class TimeoutTestControllerTest {
    
    
    String urlPrefix = "http://localhost:8443";


    /**
     * 1.connectionTimeout测试:IP无法建立连接,连接超时
     *
     * @throws Exception
     */
    @Test
    public void connectionTimeout() throws Exception {
    
    
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://192.168.0.125:8443");

        //设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000)
                .build();
        httpGet.setConfig(requestConfig);

        long startTime = System.currentTimeMillis();
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("response:" + EntityUtils.toString(response.getEntity()) + ",请求成功耗时" + (System.currentTimeMillis() - startTime));

        } catch (ConnectTimeoutException e) {
    
    
            System.err.println(e.getMessage() + ",请求失败耗时:" + (System.currentTimeMillis() - startTime));
        }
    }

    /**
     * 2.socketTimeout测试,服务端没有指定时间内任何响应,会超时
     *
     * @throws Exception
     */
    @Test
    public void socketTimeout() throws Exception {
    
    
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(urlPrefix + "/test/socket_timeout");//test/socket_timeout 3秒后才会返回数据

        //设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000).build();
        httpGet.setConfig(requestConfig);

        long startTime = System.currentTimeMillis();
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("response:" + EntityUtils.toString(response.getEntity()) + ",请求成功耗时" + (System.currentTimeMillis() - startTime));

        } catch (SocketTimeoutException e) {
    
    
            System.err.println(e.getMessage() + ",请求失败耗时:" + (System.currentTimeMillis() - startTime));
        }
    }

    /**
     * 3.socketTimeout测试:服务端隔800ms返回一点数据,不会超时
     *
     * @throws Exception
     */
    @Test
    public void socketTimeoutNo() {
    
    
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(urlPrefix + "/test/socket_timeout_2");

        //设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000)
                .build();
        httpGet.setConfig(requestConfig);

        long startTime = System.currentTimeMillis();
        try {
    
    
            httpclient.execute(httpGet);
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("response:" + EntityUtils.toString(response.getEntity()) + ",服务端每隔800ms返回一点数据,不会超时。耗时:" + (System.currentTimeMillis() - startTime));
        } catch (Exception e) {
    
    
            System.err.println("任意两个数据包之间的时间超过了1秒,则超时。耗时:" + (System.currentTimeMillis() - startTime));
        }


    }

    /**
     * 4.connectionRequestTimeout测试:指从连接管理器(例如连接池)中拿到连接的超时时间
     * <p>
     * (多线程连接池)PoolingHttpClientConnectionManager, 可以创建并管理一个连接池,为多个路由或目标主机提供连接
     */
    @Test
    public void connectionRequestTimeoutWithPoolingConnectionManager() throws InterruptedException {
    
    
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
        connManager.setMaxTotal(2);

        final CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(connManager).build();
        final HttpGet httpGet = new HttpGet(urlPrefix + "/test/connection_request_timeout");//当前接口会休眠3秒在返回,因此需要设大SocketTimeout

        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(3500).build();
        httpGet.setConfig(requestConfig);

        // 如下多线程占满连接池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
    
    
            int finalI = i;
            executorService.submit(() -> {
    
    
                try {
    
    
                    CloseableHttpResponse response = httpclient.execute(httpGet);
                    System.out.println(finalI + "=>connectionRequestTimeoutTest1:" + EntityUtils.toString(response.getEntity()));
                } catch (SocketTimeoutException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
                } catch (IOException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=IOException=>" + e.getClass().getName() + ":" + e.getMessage());
                } catch (Exception e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
                }
            });
        }

        // 在连接池占满的情况下,拿不到就会抛异常
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("main=>connectionRequestTimeoutTest2: " + EntityUtils.toString(response.getEntity()));
        } catch (SocketTimeoutException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (IOException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=IOException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (Exception e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
        }


        //休眠20S
        TimeUnit.SECONDS.sleep(20);
    }

    /**
     * 5.connectionRequestTimeout测试,指从连接管理器中拿到连接的超时时间,由于使用基本的连接管理器,连接被占用时,直接无法分配连接
     * connectionRequestTimeout并未生效,目前看来该参数只在 “ 多线程连接池 ” 奏效.
     * <p>
     * (单线程连接池)BasicHttpClientConnectionManager
     * 自HttpClient 4.3.3起,BasicHttpClientConnectionManager可用作HTTP连接管理器的最简单实现。它用于创建和管理一次只能由一个线程使用的单个连接。
     */
    @Test
    public void connectionRequestTimeoutWithBasicConnectionManager() throws Exception {
    
    
        BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager();
        final CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(connManager).setMaxConnPerRoute(1).build();
        final HttpGet httpGet = new HttpGet(urlPrefix + "/test/connection_request_timeout");

        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(100000)
                .setConnectionRequestTimeout(1000000).setSocketTimeout(1000000).build();
        httpGet.setConfig(requestConfig);

        // 如下多线程占满连接
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
    
    
            int finalI = i;
            executorService.submit(() -> {
    
    
                try (CloseableHttpResponse response = httpclient.execute(httpGet);) {
    
    
                    System.out.println(finalI + "=>connectionRequestTimeoutTest1:" + EntityUtils.toString(response.getEntity()));
                } catch (SocketTimeoutException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
                } catch (IOException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=IOException=>" + e.getClass().getName() + ":" + e.getMessage());

                } catch (Exception e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
                }
            });
        }


        // 在连接池占满的情况下,拿不到就会抛异常
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("main=>connectionRequestTimeoutTest2: " + EntityUtils.toString(response.getEntity()));
        } catch (SocketTimeoutException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (IOException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=IOException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (Exception e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
        }

        //休眠20S
        TimeUnit.SECONDS.sleep(20);
    }
}

分析每个单元测试案例

1.建立连接超时测试

  /**
     * 1.connectionTimeout测试:IP无法建立连接,连接超时
     *
     * @throws Exception
     */
    @Test
    public void connectionTimeout() throws Exception {
    
    
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://192.168.0.125:8443");

        //设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000)
                .build();
        httpGet.setConfig(requestConfig);

        long startTime = System.currentTimeMillis();
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("response:" + EntityUtils.toString(response.getEntity()) + ",请求成功耗时" + (System.currentTimeMillis() - startTime));

        } catch (ConnectTimeoutException e) {
    
    
            System.err.println(e.getMessage() + ",请求失败耗时:" + (System.currentTimeMillis() - startTime));
        }
    }

响应结果:
在这里插入图片描述

当前请求的超时配置为:

  • 连接池获取连接时间1秒,建立连接时间1秒,数据包响应时间1秒

接口 http://192.168.0.125:8443 并不存在,因此客户端与服务端建立连接时间超出connectTimeout配置时间后就会抛出ConnectTimeoutException

2.响应超时测试(单个数据包)

    /**
     * 2.socketTimeout测试,服务端没有指定时间内任何响应,会超时
     *
     * @throws Exception
     */
    @Test
    public void socketTimeout() throws Exception {
    
    
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(urlPrefix + "/test/socket_timeout");//test/socket_timeout 3秒后才会返回数据

        //设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000).build();
        httpGet.setConfig(requestConfig);

        long startTime = System.currentTimeMillis();
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("response:" + EntityUtils.toString(response.getEntity()) + ",请求成功耗时" + (System.currentTimeMillis() - startTime));

        } catch (SocketTimeoutException e) {
    
    
            System.err.println(e.getClass().getName() + ":" + e.getMessage()+"=》请求失败耗时:" + (System.currentTimeMillis() - startTime));
        }
    }

响应结果:
在这里插入图片描述

当前请求的超时配置为:

  • 连接池获取连接时间1秒,建立连接时间1秒,数据包响应时间1秒

接口 /test/socket_timeout 会休眠3秒,因此服务端响应单个数据包超出socketTimeout配置时间后就会抛出SocketTimeoutException

3.响应超时测试(多个数据包)

网络不好时,服务端可能会分段返回多个数据包时,包与包之前不超过socketTimeout时间,而整体时间超过socketTimeout时间,也不会判断为超时

    /**
     * 3.socketTimeout测试:服务端隔800ms返回一点数据,不会超时
     *
     * @throws Exception
     */
    @Test
    public void socketTimeoutNo() {
    
    
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(urlPrefix + "/test/socket_timeout_2");

        //设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(1000)
                .build();
        httpGet.setConfig(requestConfig);

        long startTime = System.currentTimeMillis();
        try {
    
    
            httpclient.execute(httpGet);
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("response:" + EntityUtils.toString(response.getEntity()) + ",服务端每隔800ms返回一点数据,不会超时。耗时:" + (System.currentTimeMillis() - startTime));
        } catch (Exception e) {
    
    
            System.err.println( e.getClass().getName() + ":" + e.getMessage()+"=》任意两个数据包之间的时间超过了1秒,则超时。耗时:" + (System.currentTimeMillis() - startTime));
        }
    }

响应结果:
在这里插入图片描述

当前请求的超时配置为:

  • 连接池获取连接时间1秒,建立连接时间1秒,数据包响应时间1秒

接口 /test/socket_timeout_2 每隔0.8秒传输一次数据,传输10次,总共8秒,这样是不超时的。而如果任意两个数据包之间的时间超过了1秒,则超时,超出后会抛出SocketTimeOutException

本人测试过: 实际上okHttp以及HttpURLConnectionsocketTimeOut 都是 连接建立后,数据传输过程中数据包之间间隔的最大时间
.
如果说错了请在下方对本文评论

4.多线程连接获取连接超时测试

    /**
     * 4.connectionRequestTimeout测试:指从连接管理器(例如连接池)中拿到连接的超时时间
     * <p>
     * (多线程连接池)PoolingHttpClientConnectionManager, 可以创建并管理一个连接池,为多个路由或目标主机提供连接
     */
    @Test
    public void connectionRequestTimeoutWithPoolingConnectionManager() throws InterruptedException {
    
    
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
        connManager.setMaxTotal(2);

        final CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(connManager).build();
        final HttpGet httpGet = new HttpGet(urlPrefix + "/test/connection_request_timeout");//当前接口会休眠3秒在返回,因此需要设大SocketTimeout

        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(1000)
                .setSocketTimeout(3500).build();
        httpGet.setConfig(requestConfig);

        // 如下多线程占满连接池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
    
    
            int finalI = i;
            executorService.submit(() -> {
    
    
                try {
    
    
                    CloseableHttpResponse response = httpclient.execute(httpGet);
                    System.out.println(finalI + "=>connectionRequestTimeoutTest1:" + EntityUtils.toString(response.getEntity()));
                } catch (SocketTimeoutException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
                } catch (IOException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=IOException=>" + e.getClass().getName() + ":" + e.getMessage());
                } catch (Exception e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
                }
            });
        }

        // 在连接池占满的情况下,拿不到就会抛异常
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("main=>connectionRequestTimeoutTest2: " + EntityUtils.toString(response.getEntity()));
        } catch (SocketTimeoutException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (IOException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=IOException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (Exception e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
        }

        //休眠20S
        TimeUnit.SECONDS.sleep(20);
    }

响应结果:

在这里插入图片描述

当前请求的超时配置为:

  • 连接池获取连接时间1秒,建立连接时间1秒,数据包响应时间3.5秒

接口 /test/connection_request_timeout 会在3秒后返回数据,导致一个连接会被占用3秒后,才能被恢复可用状态,当多个线程同时从PoolingHttpClientConnectionManager 获取连接时,无空闲连接可用且超出等待获取连接池连接时间,会抛出ConnectionPoolTimeoutException

5.单线程连接获取连接超时测试

    /**
     * 5.connectionRequestTimeout测试,指从连接管理器中拿到连接的超时时间,由于使用基本的连接管理器,连接被占用时,直接无法分配连接
     * connectionRequestTimeout并未生效,目前看来该参数只在 “ 多线程连接池 ” 奏效.
     * <p>
     * (单线程连接管理)BasicHttpClientConnectionManager
     * 自HttpClient 4.3.3起,BasicHttpClientConnectionManager可用作HTTP连接管理器的最简单实现。它用于创建和管理一次只能由一个线程使用的单个连接。
     */
    @Test
    public void connectionRequestTimeoutWithBasicConnectionManager() throws Exception {
    
    
        BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager();
        final CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(connManager).setMaxConnPerRoute(1).build();
        final HttpGet httpGet = new HttpGet(urlPrefix + "/test/connection_request_timeout");

        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(100000)
                .setConnectionRequestTimeout(1000000).setSocketTimeout(1000000).build();
        httpGet.setConfig(requestConfig);

        // 如下多线程占满连接
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
    
    
            int finalI = i;
            executorService.submit(() -> {
    
    
                try (CloseableHttpResponse response = httpclient.execute(httpGet);) {
    
    
                    System.out.println(finalI + "=>connectionRequestTimeoutTest1:" + EntityUtils.toString(response.getEntity()));
                } catch (SocketTimeoutException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
                } catch (IOException e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=IOException=>" + e.getClass().getName() + ":" + e.getMessage());

                } catch (Exception e) {
    
    
                    System.err.println(finalI + "=>connectionRequestTimeoutTest1=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
                }
            });
        }

        // 在连接池占满的情况下,拿不到就会抛异常
        try {
    
    
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println("main=>connectionRequestTimeoutTest2: " + EntityUtils.toString(response.getEntity()));
        } catch (SocketTimeoutException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=SocketTimeoutException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (IOException e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=IOException=>" + e.getClass().getName() + ":" + e.getMessage());
        } catch (Exception e) {
    
    
            System.err.println("main=>connectionRequestTimeoutTest2=Exception=>" + e.getClass().getName() + ":" + e.getMessage());
        }

        //休眠20S
        TimeUnit.SECONDS.sleep(20);
    }

响应结果:
在这里插入图片描述

当前请求的超时配置为:

  • 连接池获取连接时间1秒,建立连接时间1秒,数据包响应时间1秒

    接口 /test/connection_request_timeout 会在3秒后返回数据,导致一个连接会被占用3秒后,才能被恢复可用状态,BasicHttpClientConnectionManager 是一个简单的连接管理器,它用于创建和管理一次只能由一个线程使用的单个连接。 使用他当获取连接时超过 connectionRequestTimeout并未抛出ConnectionPoolTimeoutException,因此可以得出 connectionRequestTimeout是从 连接池获取连接的超时时间的设置


HttpClient之socketTimeOut源码解析
httpclient连接池异常引发的惨案

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/108094897