gh-4569: Deterministically close shared HTTP client on TransportClientFactory#shutdown() - #4585
Open
PRAHLAD09-dev wants to merge 1 commit into
Open
Conversation
Signed-off-by: Prahlad Bhakat <prahladbhakat05@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4569
Problem
RestClientTransportClientFactory.newClient()builds a brand-newCloseableHttpClient(with its own dedicated connection pool) on every single call. NeitherTransportClientFactory#shutdown()norEurekaHttpClient#shutdown()ever closes these clients - cleanup relied entirely on GC. During graceful shutdown, the finalunregister()DELETE call (made fromDiscoveryClient.shutdown(), right aftercancelScheduledTasks()) can hit a connection pool that's already been torn down, throwing:This causes deregistration to fail silently during graceful shutdown, leaving stale instances in the Eureka registry until the lease expires.
History
This overlaps with #4103, which documented the same "new HTTP client per call, never closed" issue. That was addressed in #4258 by caching a shared
CloseableHttpClientinDefaultEurekaClientHttpRequestFactorySupplierand making the supplier a SpringDisposableBeanto close it on shutdown. That approach was reverted (79a2eb8) after it caused #4275: making the supplier aDisposableBeanintroduced an independent Spring bean-destroy callback with no ordering guarantee relative toCloudEurekaClient's own@Bean(destroyMethod = "shutdown")- so the shared client could be closed beforeCloudEurekaClient.shutdown()reached itsunregister()call, breaking deregistration outright.Fix
Same idea as #4258 - share one
CloseableHttpClientper supplier instance instead of building one per call - but close it through the Eureka transport lifecycle instead of an independent Spring bean-destroy callback:EurekaClientHttpRequestFactorySuppliergets a newdefault void close() {}method (backward compatible for existing custom implementations).DefaultEurekaClientHttpRequestFactorySupplierlazily builds and caches a singleCloseableHttpClient, reused across allget()calls, and closes it inclose(). It does not implementDisposableBean.RestClientTransportClientFactory#shutdown()now callseurekaClientHttpRequestFactorySupplier.close().Since
TransportClientFactory#shutdown()is invoked synchronously by Netflix'sDiscoveryClient.shutdown()- which callsunregister()beforeeurekaTransport.shutdown()- the pool is now guaranteed to close after the final deregistration request completes, in the same thread, with no race against an unrelated Spring bean-destroy path.Testing
DefaultEurekaClientHttpRequestFactorySupplierTests: verifies the client is reused acrossget()calls,close()is safe to call before anyget()and safe to call twice, and - as an explicit regression guard for When shutting down after 4.1.1, an exception occurs while unregistering #4275 - that this class does not implementDisposableBean.RestClientTransportClientFactoryShutdownTests: verifiesshutdown()delegates tosupplier.close().spring-cloud-netflix-eureka-clienttest suite passes locally (216/216, excluding one pre-existing Docker-dependent Testcontainers test unrelated to this change).