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

Pav*_*hov 3 java socket-timeout-exception apache-httpcomponents apache-httpclient-4.x

我创建了一个 Apache HTTP 客户端

             CloseableHttpClient client = HttpClients.custom()
                .setMaxConnPerRoute(25)
                .setMaxConnTotal(50)
                .setDefaultRequestConfig(RequestConfig.custom()
                        .setConnectionRequestTimeout(20_000)
                        .build())
                .build();
Run Code Online (Sandbox Code Playgroud)

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

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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

我有 2 个问题

  1. 我怎样才能找到这个时间开始的地方?这些参数对我不起作用

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

ok2*_*k2c 7

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

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

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