-
Notifications
You must be signed in to change notification settings - Fork 41.7k
Description
In Spring Boot 3.5.x, when using RestClient with Apache HttpClient5, there are typically some configurations required as the defaults are not production-ready:
- Setting connect/read timeouts
- Setting the connection pool size: max-per-route/max-total (defaults are only 5/25)
Now in case I have 2 (or more) http clients in my app, I can configure my timeouts globally, as well as my factory.
spring:
http:
client:
factory: http-components
connect-timeout: 5s
read-timeout: 10sIn order to customize the Apache HttpClient5 connection pool, I can expose a ClientHttpRequestFactoryBuilderCustomizer bean, or a HttpComponentsClientHttpRequestFactoryBuilder bean:
@Bean
ClientHttpRequestFactoryBuilderCustomizer<HttpComponentsClientHttpRequestFactoryBuilder> customizer() {
return builder -> builder.withConnectionManagerCustomizer(this::customizeConnectionPool);
}
private void customizeConnectionPool(PoolingHttpClientConnectionManagerBuilder pool) {
pool.setMaxConnPerRoute(100);
pool.setMaxConnTotal(400);
}I would inject a RestClient.Builder into the constructor of each of my 2 client classes, and configure each one according to its special needs:
# In SomeClient1.java
private final RestClient restClient;
public SomeClient1(RestClient.Builder builder, SomeClient1Properties properties) {
this.restClient = builder.baseUrl(properties.baseUrl()).build();
}# In SomeClient2.java
private final RestClient restClient;
public SomeClient2(RestClient.Builder builder, SomeClient2Properties properties) {
this.restClient = builder.baseUrl(properties.baseUrl()).build();
}But now say SomeClient2 needs a different read timeout since the global default is too short for the specific service it's calling. I can adjust it as follows:
# In SomeClient2.java
private final RestClient restClient;
public SomeClient2(RestClient.Builder builder, SomeClient2Properties properties) {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.withReadTimeout(Duration.ofMinutes(1));
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder.httpComponents().build(settings);
this.restClient = builder.baseUrl(properties.baseUrl())
.requestFactory(requestFactory)
.build();
}But this change is not additive - it replaces the ClientHttpRequestFactory created by the auto-configured ClientHttpRequestFactoryBuilder that included my ClientHttpRequestFactoryBuilderCustomizer.
Finally the question: was the EAGER connection between the ClientHttpRequestFactorySettings and the ClientHttpRequestFactoryBuilder done intentionally?
Could the 2 concerns have been separated and used only in the RestClient.build() phase to allow overriding one but not the other?
private final RestClient restClient;
public SomeClient2(RestClient.Builder builder, SomeClient2Properties properties) {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.withReadTimeout(Duration.ofMinutes(1));
// pseudo-code - override settings but use auto-configured ClientHttpRequestFactoryBuilder
this.restClient = builder.baseUrl(properties.baseUrl())
.requestSettings(settings)
.build();
}