Repository navigation
fix(bootstrap): own and dispose the gateway's custom Netty event loops (#7715) - #7726
Merged
Merged
Conversation
Aias00
approved these changes
Oct 17, 2026
Aias00
left a comment
Contributor
There was a problem hiding this comment.
LGTM. Ownership now has an owner, which is the whole point.
Evidence
LoopResourcesextendsDisposable, so@Bean(destroyMethod = "dispose")is the correct lifecycle hook, andDisposable.dispose()in reactor-netty is idempotent (DefaultLoopResourcesguards it with a flag), so a double dispose cannot throw.- Destroy ordering is safe:
nettyReactiveWebServerFactorytakes the bean as a constructor argument, so the bean is destroyed after the factory, and Spring Boot'sWebServerStartStopLifecyclestops the server during context close before singleton destruction. The loops therefore always outlive the server that uses them. - Server restart inside a live context (the case you call out) is also fine: the bean is only disposed at context close, so
runOnstill receives usable loops. - The test is the right shape —
ApplicationContextRunnerasserts not-disposed insiderun()and disposed after it returns, and you verified the red-green by removingdestroyMethod. - Both beans carry the same
@ConditionalOnProperty(..., matchIfMissing = true), so the factory can never ask for aLoopResourcesbean that was not created.
Non-blocking
- Worth one sentence in the PR body confirming the interaction with reactor-netty: a
LoopResourcessupplied throughrunOnis not disposed by the server (only resources fromHttpResourcesare), which is exactly why this bean is needed. That is the assumption the whole fix rests on. shenyuServerLoopResources(ObjectProvider<NettyHttpProperties>)falls back tonew NettyHttpProperties()when no properties bean exists — same pattern as the factory, so consistent. Just note that the fallback means two independent instances could in theory produce different select/worker counts; they do not today because both read the same provider.
CI: pr_build green.
Contributor
Author
|
Done. Note added to the PR body — a |
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 #7715.
Summary
EventLoopNettyCustomizer#applycreates a newLoopResourceson every call and hands it toHttpServer#runOn, but nothing owns it: Spring Boot's server stop closes the bound server without disposing loops supplied by a customizer, so a stopped/restarted embedded server leaves its event-loop threads and resources behind.Modifications
shenyuServerLoopResourcesbean with@Bean(destroyMethod = "dispose"), created fromNettyHttpProperties(same select/worker counts and conditions as before).EventLoopNettyCustomizerand use.runOn(loopResources)instead of allocating a newLoopResourcesinapply().Verifying this change
New
ShenyuNettyWebServerConfigurationTest#testEventLoopResourcesDisposedWithContext: insideApplicationContextRunner,isDisposed()is false; afterrun()returns (context closed),isDisposed()is true. RemovingdestroyMethod = "dispose"makes this assertion fail (Expected: is <true>), restoring it passes.Targeted tests pass:
./mvnw -B -ntp -pl shenyu-spring-boot-starter/shenyu-spring-boot-starter-gateway test -Dtest=ShenyuNettyWebServerConfigurationTest4/4 green.
Checkstyle passes:
clean.
Notes
One
LoopResourcesinstance per application context: server stop/start cycles inside the same context reuse it, and it is disposed exactly once when the context closes, so loops shared by a still-running server are never disposed early. Select/worker counts and the daemon flag are unchanged.Review follow-up
A
LoopResourcessupplied throughrunOnis not disposed by the embedded server — only Reactor Netty's sharedHttpResourcesare — which is why the loop resources need an owner; the context-managed bean introduced here provides it.