-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Description
Spring Cloud 2021.0.0
Spring Boot 2.6.8
Bug Description:
RestTemplateEurekaHttpClient is not closing HttpClient on shutdown. This leads to TCP CLOSE_WAIT connections to eureka server.
EurekaClient will shutdown when an exception occurs on an http request, but not shutdown HttpClient.
This bug is related to
Line 203 in 27ac337
| // Nothing to do |
In case of exception here:
https://github.com/Netflix/eureka/blob/ed0da19ca1c049c87e3dbf75b6015c1861d5c2d0/eureka-client/src/main/java/com/netflix/discovery/shared/transport/decorator/RedirectingEurekaHttpClient.java#L96
new HttpClient will be created without closing the existing one - this causes CLOSE_WAIT connections
This supplier creates new CloseableHttpClient for every call to
Line 103 in 27ac337
| public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) { |
public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClientHttpRequestFactorySupplier {
@Override
public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier) {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
if (sslContext != null) {
httpClientBuilder = httpClientBuilder.setSSLContext(sslContext);
}
if (hostnameVerifier != null) {
httpClientBuilder = httpClientBuilder.setSSLHostnameVerifier(hostnameVerifier);
}
CloseableHttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
}
}
so in case of shutdown, currentEurekaClient shutdown don't closes connections:
Line 203 in 27ac337
| // Nothing to do |
possible solution will be trying to close the HttpClient:
shutdown could be
public void shutdown() {
Optional.of(unwrapRequestFactoryIfNecessary(restTemplate.getRequestFactory()))
.filter(HttpComponentsClientHttpRequestFactory.class::isInstance)
.map(HttpComponentsClientHttpRequestFactory.class::cast)
.ifPresent(requestFactory-> {
try {
requestFactory.destroy();
} catch (Exception e) {
}
});
}
unwrapRequestFactoryIfNecessary
https://github.com/spring-projects/spring-boot/blob/47516b50c39bd6ea924a1f6720ce6d4a71088651/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java#L746
spring-projects/spring-boot#31075
As you can see process open files increases over time until GC occurred

We can also create EurekaClientHttpRequestFactorySupplier which return the same ClientHttpRequestFactory, but it not a stable solution since we can't control eureka client code, maybe in case of exceptions connections not closes (hence not returns to PoolingHttpClientConnectionManager) - this can lead to no available connections in pool. In that case, restart is required
Original Issue:
#4062
