application.yml:

asimiotech:
  resttemplate:
    max-total: 20
    default-max-per-route: 20
    conn-timeout-millis: 4000
    conn-request-timeout-millis: 4000
    socket-timeout-millis: 4000
    routes:
      -
        scheme: http
        host: localhost
        port: 8000
        max-per-route: 20

RestTemplateProperties.java:

@Configuration
@ConfigurationProperties(prefix = "asimiotech.resttemplate")
@Getter
@Setter
public class RestTemplateProperties {

  private Integer maxTotal;
  private Integer defaultMaxPerRoute;
  private Integer connTimeoutMillis;
  private Integer connRequestTimeoutMillis;
  private Integer socketTimeoutMillis;
  private List<RouteProperties> routes;

  @Getter
  @Setter
  public static class RouteProperties {

    private String scheme;
    private String host;
    private Integer port;
    private Integer maxPerRoute;

  }

}

RestTemplateConfig.java:

@Configuration
@EnableConfigurationProperties(RestTemplateProperties.class)
@RequiredArgsConstructor
public class RestTemplateConfig {

  private final RestTemplateProperties restTemplateProperties;

  @Bean
  public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
    PoolingHttpClientConnectionManager result = new PoolingHttpClientConnectionManager();
    result.setMaxTotal(this.restTemplateProperties.getMaxTotal());
    // Default max per route is used in case it's not set for a specific route
    result.setDefaultMaxPerRoute(this.restTemplateProperties.getDefaultMaxPerRoute());

    // Adding individual route configuration when they exist
    this.restTemplateProperties.getRoutes().stream()
      .forEach(route -> this.addNewRoute(result, route));

    return result;
  }

  @Bean
  public RequestConfig requestConfig() {
    return RequestConfig.custom()
      .setConnectionRequestTimeout(this.restTemplateProperties.getConnRequestTimeoutMillis(), TimeUnit.MILLISECONDS)
      .setConnectTimeout(this.restTemplateProperties.getConnTimeoutMillis(), TimeUnit.MILLISECONDS)
      .setResponseTimeout(this.restTemplateProperties.getSocketTimeoutMillis(), TimeUnit.MILLISECONDS)
      .build();
  }

  @Bean
  public CloseableHttpClient apacheHttpClient(
    PoolingHttpClientConnectionManager poolingHttpClientConnectionManager,
    RequestConfig requestConfig) {

    CloseableHttpClient result = HttpClientBuilder.create()
      .setConnectionManager(poolingHttpClientConnectionManager)
      .setDefaultRequestConfig(requestConfig)
      .build();
    return result;
  }

  @Bean
  public RestTemplate restTemplate(HttpClient httpClient) {
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    return new RestTemplate(requestFactory);
  }

  private void addNewRoute(PoolingHttpClientConnectionManager connectionManager, RouteProperties route) {
    HttpHost host = new HttpHost(route.getScheme(), route.getHost(), route.getPort());
    // Max per route for a specific host route
    connectionManager.setMaxPerRoute(new HttpRoute(host), route.getMaxPerRoute());
  }

}


Usage

ClassThatSendsHttpRequests.java:

@RequiredArgsConstructor
public class ClassThatSendsHttpRequests {

  private final RestTemplate restTemplate;

// ...
}