HttpClient使用时Timeout waiting for connection from pool,maxConnTotal和maxConnPerRoute

一、为什么会报Timeout waiting for connection from pool

首先我们需要知道,HttpClient是不建议每次使用都创建的,因为它本身就带一个连接池。如果我们使用频繁的话,频繁创建HttpClient对象也不是明智的。

我在使用HttpClient的时候,在一个调用链中,只是依次使用了HttpClient调用了几次http接口,却发现在第5个的时候,报错了
org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

这是我当时使用的配置:

  private static HttpClient client;

  static {
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(5000)
            .setConnectionRequestTimeout(3000)
            .setSocketTimeout(5000).build();
    client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
            .build();
  }

查了下才知道这个错,是因为连接池没有空闲连接,并且等待获取连接的时间达到了setConnectionRequestTimeout,我很疑惑为啥才一个单线程使用了几下就报错了呢?于是我开始了测试。

二、测试

这里先说结论

HttpClient默认连接池,最大连接是20个,最大同路由的是2个,也就是maxConnTotal是20,maxConnPerRoute是2。

maxConnTotal是同时间正在使用的最多的连接数
maxConnPerRoute是针对一个域名同时间正在使用的最多的连接数

看源码

可以看org.apache.http.impl.conn.PoolingHttpClientConnectionManager类,

public PoolingHttpClientConnectionManager(HttpClientConnectionOperator httpClientConnectionOperator, HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, long timeToLive, TimeUnit timeUnit) {
    this.log = LogFactory.getLog(this.getClass());
    this.configData = new PoolingHttpClientConnectionManager.ConfigData();
    this.pool = new CPool(new PoolingHttpClientConnectionManager.InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, timeUnit);
    this.pool.setValidateAfterInactivity(2000);
    this.connectionOperator = (HttpClientConnectionOperator)Args.notNull(httpClientConnectionOperator, "HttpClientConnectionOperator");
    this.isShutDown = new AtomicBoolean(false);
  }

验证1

先前5个调用百度,后5次调用hao123

package com.zgd.demo.web.test;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test;

import java.io.IOException;

/**
 * @Author: zgd
 * @Date: 2019/8/15 00:04
 * @Description: 测试httpclient
 */
@Slf4j
public class MyHttpClientTest {

  private static HttpClient client;

  static {
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(5000)
            .setConnectionRequestTimeout(3000)
            .setSocketTimeout(5000).build();
    client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
            .build();
  }


  @Test
  public void fun01()  {
    for (int i = 0; i < 10; i++) {
      String url = "";
      if (i < 5){
        url = "http://www.baidu.com";
      }else {
        url = "http://www.hao123.com";
      }
      HttpGet httpGet = new HttpGet(url);
      log.info("------url:{}-----",url);
      HttpResponse res = null;
      try {
        res = client.execute(httpGet);
      } catch (IOException e) {
        log.error("异常: ",e);
        return;
      }
      int code = res.getStatusLine().getStatusCode();
      log.info("---url:{}\tcode:{}\ti:{}",url, code,i+1);

    }

  }

}

运行结果
在这里插入图片描述
在第3个,i=2的时候就跑这个异常了,在debug级别日志打印出total kept alive: 0; route allocated: 2 of 2; total allocated: 2 of 20,也就是这个route下一共2个连接,已经使用2个。整个pool有20个连接,使用了2个

验证2

依次调用百度和hao123


  @Test
  public void fun01()  {
    for (int i = 0; i < 10; i++) {
      String url = "";
      if (i % 2 == 0){
        url = "http://www.baidu.com";
      }else {
        url = "http://www.hao123.com";
      }
      HttpGet httpGet = new HttpGet(url);
      log.info("------url:{}-----",url);
      HttpResponse res = null;
      try {
        res = client.execute(httpGet);
      } catch (IOException e) {
        log.error("异常: ",e);
        return;
      }
      int code = res.getStatusLine().getStatusCode();
      log.info("---url:{}\tcode:{}\ti:{}",url, code,i+1);

    }

  }

在这里插入图片描述

验证3

我们修改代码如下

@Test
  public void fun01()  {
    for (int i = 0; i < 10; i++) {
      String url = "";
      if (i % 2 == 0){
        url = "http://www.baidu.com";
      }else {
        url = "http://www.hao123.com";
      }
      HttpGet httpGet = new HttpGet(url);
      log.info("------url:{}-----",url);
      HttpResponse res = null;
      try {
        res = client.execute(httpGet);
        int code = res.getStatusLine().getStatusCode();
        log.info("---url:{}\tcode:{}\ti:{}",url, code,i+1);
      } catch (IOException e) {
        log.error("异常: ",e);
        return;
      }finally {
        if (res != null){
          EntityUtils.consumeQuietly(res.getEntity());
        }
      }
    }
  }

不管是先百度后好123,还是依次调用,都完全ok了
这里使用了HttpClient提供的工具类EntityUtils来处理,EntityUtils的consume方法都对连接资源进行了释放。

 public static void consumeQuietly(HttpEntity entity) {
    try {
      consume(entity);
    } catch (IOException var2) {
    }

  }
...

 public static void consume(HttpEntity entity) throws IOException {
    if (entity != null) {
      if (entity.isStreaming()) {
        InputStream inStream = entity.getContent();
        if (inStream != null) {
          inStream.close();
        }
      }

    }
  }

三、结果

可以看出,出现Timeout waiting for connection from pool异常的原因主要有二:

  1. 连接没关闭,资源没释放。
  2. pool的连接数设置太小

可以看到第1条,是老生常谈的资源关闭问题。博主在开发的时候,正是因为不需要知道调用的返回结果,所以没有对Response进行处理,所以资源并没有释放,导致后面再使用HttpClient,就拿不到连接了

第2条,需要知道maxConnTotal和maxConnPerRoute的具体含义,如果是需要频繁调用同一个域名,那么即使maxConnTotal设置再大,还是受限制与maxConnPerRoute

根据业务实际情况合理配置连接池

 private static HttpClient client;

  static {
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(5000)
            .setConnectionRequestTimeout(1000)
            .setSocketTimeout(5000).build();
    client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
            .setMaxConnTotal(200)
            .setMaxConnPerRoute(50)
            .build();
  }
  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
org.apache.http.conn.ConnectionPoolTimeoutException: timeout waiting for connection 是指在使用 Apache HttpClient 进行网络请求连接池中的连接等待超的异常。 当使用 Apache HttpClient 进行网络请求,可以将一些连接放入连接池中,以便在下次请求复用。这样可以减少建立和关闭连接的开销,提高请求的性能。然而,连接池有一个最大容量,当连接池已满,新的请求需要等待已有的连接被释放到连接池中。 当出现 org.apache.http.conn.ConnectionPoolTimeoutException 异常,意味着在尝试获取连接发生了超。可能是因为连接池中的连接数已满,且所有的连接都在使用中,没有空闲的连接可以提供给新的请求。因此,新的请求必须等待连接池中的连接被释放或者超。 这种情况通常发生在网络负载较大、请求频繁的情况下,或者服务器响应间较长的情况下。为了解决这个问题,可以考虑增大连接池的最大容量,或者优化请求的设计,减少请求的频率。另外,可以尝试调整连接超间,以适应网络环境的差异。 总之,org.apache.http.conn.ConnectionPoolTimeoutException: timeout waiting for connection 异常表示在使用 Apache HttpClient 进行网络请求连接池中的连接超等待的异常。需要根据具体情况,调整连接池的配置或者请求的设计,以解决该异常并提高请求的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值