application.yml:

asimiotech:
  http-client-pool:
    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

HttpClientProperties.java:

@ConfigurationProperties(prefix = "asimiotech.http-client-pool")
@Getter
@Setter
public class HttpClientProperties {

  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;

  }

}

WebClientConfig.java:

@Configuration
@EnableConfigurationProperties(HttpClientProperties.class)
@RequiredArgsConstructor
public class WebClientConfig {

  private final HttpClientProperties httpClientProperties;

  @Bean
  public RestClient restClient() {
    return RestClient.builder()
      .requestFactory(this.clientHttpRequestFactory())
      .build();
  }

  @Bean
  public ClientHttpRequestFactory clientHttpRequestFactory() {
    HttpComponentsClientHttpRequestFactory result = new HttpComponentsClientHttpRequestFactory();
    result.setHttpClient(this.apacheHttpClient());
    return result;
  }

  @Bean
  public CloseableHttpClient apacheHttpClient() {
    CloseableHttpClient result = HttpClientBuilder
      .create()
      .setConnectionManager(this.poolingHttpClientConnectionManager())
      .setDefaultRequestConfig(this.requestConfig())
      .build();
    return result;
  }

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

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

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

    return result;
  }

  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 RestClient restClient;

// ...
}