HttpClient:Timeout waiting for connection from pool

项目中访问外网http接口的逻辑,然后就直接用Apache的HttpClient包做了一个工具类进行调用,但项目上线后发现有大bug,报错:response stream exception,org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool,错误是Apache包中直接报出的,说明是我调用方法或流程存在问题。

问题代码:

此为我包装的工具类

@Slf4j
public class HttpUtils {
    private static CloseableHttpClient httpclient = HttpClients.createDefault();
    private static final String CHARSET = "UTF-8";
    public static CloseableHttpResponse httpPost(String url, String jsonArg, Map<String,String> header) {
        HttpPost httppost = new HttpPost(url);
        HttpEntity httpEntity = new StringEntity(jsonArg, CHARSET);
        httppost.setEntity(httpEntity);
        if (header != null) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                httppost.setHeader(entry.getKey(), entry.getValue());
            }
        }
        httppost.setHeader("Content-Type", "application/json");
        log.info("request url:{},request headers:{},request body:{}", url,
Lists.newArrayList(httppost.getAllHeaders()).toString(), jsonArg);
        CloseableHttpResponse response;
        try {
            response = httpclient.execute(httppost);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return response;
            }
        } catch (IOException e) {
            log.error("response stream exception,{}", e);
        }
        return null;
    }
}

问题重现:

写循环调用本地返回不为200的http接口,如下

public class TestMain {
    private static final String URL = "http://127.0.0.1:8080/";
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            CloseableHttpResponse response = HttpUtils.httpPost(URL, null, null);
            if (response == null) {
                System.out.println("第" + i + "次-------------请求失败---------");
            } else {
                System.out.println("第" + i + "次" + response.getStatusLine().getStatusCode());
                
            }
        }
    }
}

结果发现错误重现,如下:

从第四次请求开始出现Timeout waiting for connection from pool

用netstat命令查看本机TCP占用,发现均是“已建立连接”的状态

问题分析:

连接池Timeout 是因为没有连接被释放,或者并发不够用

我的项目没有并发,初步确定是没有释放

那么为什么连接池没有释放连接呢?

初步思考:

1. 既然是连接池,肯定会有自动回收连接的机制

2. 肯定是我的代码处理,导致没有回收

3. 代码里需要关闭的有两个对象,一个是CloseableHttpClient,一个是CloseableHttpResponse

查看代码发现只有当response.getStatusLine().getStatusCode() == HttpStatus.SC_OK 也就http返回码为200时才对response进行返回上一层进行处理,否则直接无视response

问题验证:

无论什么情况都对response进行显示关闭,代码如下,增加response的关闭

结果确实没有再出现Timeout waiting for connection from pool错误

问题结论:

1. 因为我只有当返回码为200时才把response返回给上一层进行处理,response处理之后会认为消费用而自动释放,如没有被消费则一直保持连接。只要获取了response的输入流,即认为被消费,如下的工具类的实现就是对输入流的处理。其实最终的原因就是http的输入流没有被处理而资源不释放。

String entity = EntityUtils.toString(response.getEntity(), "UTF-8");

2. 当连接成功,但返回码不是200时,比如404。我的代码是直接无视response,这样的话response没有被消费,而连接会一直为此保存。当连接不成功时,其实就没有response的生成,连接则会自动回收。

3. 在以前的直接用java的http工具类时,因为是直接对输入流处理,所以都会主动去关闭连接关闭流,而不存在此问题

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值