javaapache-httpclient-4.xapache-httpcomponentssocket-timeout-exception

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


I created an Apache HTTP client

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

I am using it as a singleton. I have this method for sending requests:

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);
        }
    }

It works fine. But after some time (5-6 min) of idle, if I send a request I get "java.net.SocketException: Connection reset"

I have 2 questions

  1. How can I find a place where this time is outset? those parameters don't work for me

                 .setSocketTimeout(25_000)
                 .setConnectTimeout(26_000)
    
  2. What is the best way to fix this problem? (I mean retry request or change timeout or reconnect after or before each request)


Solution

  • This is a general limitation of the classic (blocking) i/o: blocking connections cannot react to any i/o events when idle (not engaged in an i/o operations). Likewise, timeout settings have no effect on idle connections.

    There are several defensive measures one can employ to minimize the chances of a persistent connection reset:

    1. Validate persistent connections upon lease from the connection pool after a certain period of inactivity (for example, after 1s)
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setValidateAfterInactivity(1000);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setConnectionManager(cm)
                .build();
    
    1. Proactively evict expired connections and those that have been idle over a certain period of time (for example, 5s) from the connection pool
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setValidateAfterInactivity(1000);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setConnectionManager(cm)
                .evictExpiredConnections()
                .evictIdleConnections(5L, TimeUnit.SECONDS)
                .build();
    
    1. If everything else fails, retry requests that are safe to retry. One might want to use a custom HttpRequestRetryHandler implementation instead of the default one when more control is necessary.
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setValidateAfterInactivity(1000);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setConnectionManager(cm)
                .setRetryHandler(DefaultHttpRequestRetryHandler.INSTANCE)
                .build();
    
    1. Evict connections from the pool manually when expecting a long period inactivity. This is what HttpClient does for you automatically when configured with evictIdleConnections method at the cost of an extra monitor thread.
        cm.closeIdleConnections(0, TimeUnit.MICROSECONDS);