Describe the bug spring cloud 2021.0.0 Eureka Client has a bug, the bug is that every time the heartbeat and request Eureka Server will create a new HttpClient, but this new HttpClient will not be manually closed, only when the FullGC will be cleaned up, so it leads to the problem of too much TCP CLOSE_WAIT.

Sample The solution is to provide a shared HttpClient, rather than re-creating a new one for each request.

Comment From: OlgaMaciaszek

Hello, @Ive4 thanks for submitting this. I'm not sure what you mean. In the CloudEurekaClient that handles publishing the HeartbeatEvent, this method is called that should reuse an existing EurekaHttpClient. Could you please point me to the code you have in mind or provide a minimal, complete, verifiable example that reproduces the issue along with the steps to reproduce?

Comment From: Ive4

Sorry, I didn't make it clear. Eureka publishing HeartbeatEvent will invoke RedirectingEurekaHttpClient.execute. EurekaClient will shutdown when an exception occurs on an http request, but not shutdown HttpClient

Comment From: OlgaMaciaszek

Hello, @Ive4 if I understand correctly, the issue concerns the RedirectingEurekaHttpClient itself rather than its Spring integration provided by Spring Cloud? If so, please create an issue in the Eureka repo instead.

Comment From: spring-cloud-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-cloud-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

Comment From: BenEfrati

Hi @OlgaMaciaszek , @Ive4 is right

EurekaClient will shutdown when an exception occurs on an http request, but not shutdown HttpClient

This bug is related to https://github.com/spring-cloud/spring-cloud-netflix/blob/27ac3379f6b391ccd605d212512f15c439825ecb/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java#L203

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

I'll try to explain the issue, let me know if you still want complete mvce. This supplier creates new CloseableHttpClient for every call to https://github.com/spring-cloud/spring-cloud-netflix/blob/27ac3379f6b391ccd605d212512f15c439825ecb/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java#L103

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: https://github.com/spring-cloud/spring-cloud-netflix/blob/27ac3379f6b391ccd605d212512f15c439825ecb/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java#L203

as @Ive4 said, only Full GC closes the opened connections.

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