【问题标题】:Apache HttpClient throws java.net.SocketException: Connection reset if I use it as singletoneApache HttpClient 抛出 java.net.SocketException:如果我将其用作单例,则连接重置
【发布时间】:2021-11-30 20:45:06
【问题描述】:

我创建了一个 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. 解决此问题的最佳方法是什么? (我的意思是在每个请求之后或之前重试请求或更改超时或重新连接)

【问题讨论】:

  • 你可能需要配置你的连接池来测试连接

标签: java apache-httpclient-4.x apache-httpcomponents socket-timeout-exception


【解决方案1】:

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

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

  1. 在一段时间不活动后(例如,1 秒后)从连接池租用时验证持久连接
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
    cm.setValidateAfterInactivity(1000);
    CloseableHttpClient httpclient = HttpClients.custom()
            .setConnectionManager(cm)
            .build();
  1. 主动从连接池中逐出过期连接和闲置一段时间(例如 5 秒)的连接
    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);

【讨论】:

  • 你能描述一下,它是如何工作的吗?我的意思是,我使用连接池/我在每次请求后关闭响应。为什么httpClient关闭后还保持连接?
  • 谢谢!但是我还是不明白关闭response.close()是什么意思
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-20
  • 1970-01-01
  • 2010-09-08
相关资源
最近更新 更多