afeicool
5/13/2019 - 7:20 AM

RestTemplate backed by Apache HttpClient Connection Pool. Addresses RestTemplate HttpConnection exhaustion. Note this implementation is not

RestTemplate backed by Apache HttpClient Connection Pool. Addresses RestTemplate HttpConnection exhaustion. Note this implementation is not route tunable and should be expanded when tuning by route is required. Example error: I/O error on POST request for "https://someendpoint": Timeout waiting for connection from pool; nested exception is org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ClientHttpPoolConfiguration {

    @Autowired
    private HttpConnectionPoolConfiguration poolConfiguration;

    @Bean
    public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager result = new PoolingHttpClientConnectionManager();
        result.setDefaultMaxPerRoute(poolConfiguration.getMaxPerRoute());
        result.setMaxTotal(poolConfiguration.getMaxTotal());
        return result;
    }

    @Bean
    public RequestConfig requestConfig() {
        RequestConfig result = RequestConfig.custom()
                .setConnectionRequestTimeout(poolConfiguration.getConnectionRequestTimeout())
                .setConnectTimeout(poolConfiguration.getConnectTimeout())
                .setSocketTimeout(poolConfiguration.getSocketTimeout())
                .build();
        return result;
    }

    @Bean
    public CloseableHttpClient httpClient(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager,
                                          RequestConfig requestConfig) {
        CloseableHttpClient result = HttpClientBuilder
                .create()
                .setConnectionManager(poolingHttpClientConnectionManager)
                .setDefaultRequestConfig(requestConfig)
                .build();
        return result;
    }
}
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfiguration {

    @Bean
    public RestTemplate restTemplate(HttpClient httpClient) {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new
                HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setHttpClient(httpClient);
        RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
        restTemplate.setErrorHandler(new CustomErrorResponseHandler());
        return restTemplate;
    }
}
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "httppool")
@Data
public class HttpConnectionPoolConfiguration {

    private Integer maxPerRoute;
    private Integer maxTotal;
    private Integer connectionRequestTimeout;
    private Integer connectTimeout;
    private Integer socketTimeout;

}
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.3</version>
</dependency>
httppool:
  maxPerRoute: 100
  maxTotal: 1000
  connectionRequestTimeout: 300
  connectTimeout: 300
  socketTimeout: 1500