Apache HttpClient 抛出 java.net.SocketException:如果我将其用作单例,连接将重置

Apache HttpClient throws java.net.SocketException: Connection reset if I use it as singletone

我创建了一个 Apache HTTP 客户端

             CloseableHttpClient client = HttpClients.custom()
                .setMaxConnPerRoute(25)
                .setMaxConnTotal(50)
                .setDefaultRequestConfig(RequestConfig.custom()
                        .setConnectionRequestTimeout(20_000)
                        .build())
                .build();

我将其用作单例。我有这种发送请求的方法:

public RestResponse sendPost(String serverUrl, String body) throws RestException {
        try {
            HttpPost httpPost = new HttpPost(serverUrl);
            httpPost.setEntity(new StringEntity(body));

            try (CloseableHttpResponse response = client.execute(httpPost)) {
                RestResponse restResponse = new RestResponse();
                restResponse.setCode(response.getStatusLine().getStatusCode());
                restResponse.setBody(EntityUtils.toString(response.getEntity()));
                return restResponse;
            }
        } catch (Exception e) {
            throw new RestException(e);
        }
    }

它工作正常。但是在闲置一段时间(5-6 分钟)后,如果我发送请求,我会得到 "java.net.SocketException: Connection reset"

我有 2 个问题

  1. 如何找到这个时间开始的地方?这些参数对我不起作用

                 .setSocketTimeout(25_000)
                 .setConnectTimeout(26_000)
    
  2. 解决此问题的最佳方法是什么? (我的意思是在每次请求之后或之前重试请求或更改超时或重新连接)

这是经典(阻塞)i/o 的一般限制:阻塞连接在空闲时无法对任何 i/o 事件做出反应(未参与 i/o 操作)。同样,超时设置对空闲连接没有影响。

可以采用多种防御措施来最大程度地减少持久连接重置的可能性:

  1. 在一段时间不活动后(例如,1 秒后)从连接池租用时验证持久连接
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
    cm.setValidateAfterInactivity(1000);
    CloseableHttpClient httpclient = HttpClients.custom()
            .setConnectionManager(cm)
            .build();
  1. 主动从连接池中驱逐过期连接和空闲超过一定时间(例如5s)的连接
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
    cm.setValidateAfterInactivity(1000);
    CloseableHttpClient httpclient = HttpClients.custom()
            .setConnectionManager(cm)
            .evictExpiredConnections()
            .evictIdleConnections(5L, TimeUnit.SECONDS)
            .build();
  1. 如果其他一切都失败了,请重试可以安全重试的请求。当需要更多控制时,可能希望使用自定义 HttpRequestRetryHandler 实现而不是默认实现。
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
    cm.setValidateAfterInactivity(1000);
    CloseableHttpClient httpclient = HttpClients.custom()
            .setConnectionManager(cm)
            .setRetryHandler(DefaultHttpRequestRetryHandler.INSTANCE)
            .build();
  1. 当预计长时间不活动时,手动从池中逐出连接。这是 HttpClient 在配置 evictIdleConnections 方法时自动为您执行的操作,但需要额外的监视器线程。
    cm.closeIdleConnections(0, TimeUnit.MICROSECONDS);