ConnectionPoolTimeoutException: Timeout waiting for connection from pool的错误排查及解决

ConnectionPoolTimeoutException: Timeout waiting for connection from pool的错误排查及解决


昨天遇到一个特别奇怪的事情,在系统中本来有个外部接口的方法,运行了好几年,一直正常,突然昨天开始出现异常,并且异常也比较奇怪,排查了好长时间。先看下原始代码:

    static {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5000)
                .setConnectionRequestTimeout(2000)
                .setSocketTimeout(5000).build();
        defaultHttpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
                .setMaxConnTotal(200)
                .setMaxConnPerRoute(100)
                .build();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                defaultHttpClient.close();
            } catch (IOException ignored) {
            }
        }));
    }
    public static String httpGet(String url) {
        //先创建一个HttpGet对象,传入目标的网络地址,然后调用HttpClient的execute()方法即可:
        HttpGet httpGet = new HttpGet();
        httpGet.setURI(URI.create(url));
        HttpResponse response = null;
        String resp = "";
        try {
            response = defaultHttpClient.execute(httpGet);
            // 取响应的结果
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                resp = EntityUtils.toString(response.getEntity(), "utf-8");
            }
        } catch (IOException e) {
            logger.error("httpGet error:", e);
        }
        return resp;
    }

代码很简单,但是在调用httpGet这个方法的时候,调用一段时间,就出现这个问题。开始以为是并发量太大,但是看流量也没有很大的并发,我们模拟了100个并发,也没有问题,一时不知道怎么排查。然后我们再响应码加了debug日志,生产环境又运行了一段时间,又开始报错,导致客户端接口调用直接失败。我们再仔细观察日志,发现响应码statusCode 有很多500,我们就考虑是不是因为这个的原因呢。
我们查看了这段时间statusCode =500的请求数量,恰好是等于100,而我们出现异常的情况正好是statusCode =500的第100条之后发生的,至此我们明白了,是因为在statusCode =500代码没有做任何处理,导致连接没有释放给连接池。而statusCode == HttpStatus.SC_OK,我们进行了response.getEntity(),这源码中是有关闭连接的方法的。所以我们做了以下修改:

    public static String httpGet(String url) {
        //先创建一个HttpGet对象,传入目标的网络地址,然后调用HttpClient的execute()方法即可:
        HttpGet httpGet = new HttpGet();
        httpGet.setURI(URI.create(url));
        HttpResponse response = null;
        String resp = "";
        try {
            response = defaultHttpClient.execute(httpGet);
            // 取响应的结果
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                resp = EntityUtils.toString(response.getEntity(), "utf-8");
            } else {
                httpGet.releaseConnection();
            }
        } catch (Exception e) {
            httpGet.releaseConnection();
            logger.error("httpGet error:", e);
        } finally {
            httpGet.abort();
        }
        return resp;
    }

然后模拟了200次的500的请求,系统依然正常,没有再报错。

你可能感兴趣的:(常见问题,java,connect,pool,bug)