-
Notifications
You must be signed in to change notification settings - Fork 664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support a handler for checking connection status using Ping frame in HTTP/2 #3612
base: 1.2.x
Are you sure you want to change the base?
Conversation
171dfc5
to
202d036
Compare
18196bd
to
231b27d
Compare
reactor-netty-http/src/main/java/reactor/netty/http/Http2SettingsSpec.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/Http2SettingsSpec.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/Http2SettingsSpec.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/Http2SettingsSpec.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/Http2ConnectionLivenessHandler.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/Http2ConnectionLivenessHandler.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/Http2ConnectionLivenessHandler.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConfig.java
Outdated
Show resolved
Hide resolved
...y-http/src/test/java/reactor/netty/resources/PooledConnectionProviderDefaultMetricsTest.java
Show resolved
Hide resolved
reactor-netty-http/src/test/java/reactor/netty/http/client/HttpClientTest.java
Show resolved
Hide resolved
@raccoonback Can you rebase against branch |
231b27d
to
a06c842
Compare
@violetagg |
Signed-off-by: raccoonback <[email protected]>
… in HTTP/2 Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
…e ping interval - ref. https://github.com/reactor/reactor-netty/actions/runs/13095356929/job/36537031690?pr=3612 Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
- Added a method to configure the execution interval of the scheduler that sends HTTP/2 PING frames and periodically checks for ACK responses - Introduced a retry threshold setting to limit the number of PING transmission attempts before considering the connection as unresponsive - Default values: - Scheduler interval must be explicitly set - Retry threshold defaults to 0 (no retries, only one PING attempt) Signed-off-by: raccoonback <[email protected]>
a06c842
to
8c4cdbc
Compare
@raccoonback I'll review this PR later this week |
@raccoonback Nice idea! I think that we can reuse our current I think this feature is interesting for both server and client, similar to TCP keep-alive that we support for both of them. |
@violetagg
Looking forward to your feedback! 😊 |
Yes
Yes
Yes
We may need to move I think that this feature is interesting for both client and server. Wdyt? |
Thank you for the detailed explanation! |
@violetagg |
- Added support for HTTP/2 PING-based health checks in IdleTimeoutHandler - Ensures connections remain active during health checks Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
@violetagg |
Signed-off-by: raccoonback <[email protected]>
if (isPingIntervalConfigured()) { | ||
if (pingScheduler == null) { | ||
isPingAckPending = false; | ||
pingAckDropCount = 0; | ||
pingScheduler = ctx.executor() | ||
.schedule( | ||
new PingTimeoutTask(ctx), | ||
pingAckTimeoutNanos, | ||
NANOSECONDS | ||
); | ||
} | ||
|
||
return; | ||
} | ||
|
||
ctx.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scheduler for checking the connection status based on HTTP/2 Ping frames operates only when pingAckTimeout
, pingScheduleInterval
, and pingAckDropThreshold
are all configured.
If they are not configured, the channel is closed immediately.
public void receive(Object msg) { | ||
if (msg instanceof Http2PingFrame) { | ||
Http2PingFrame frame = (Http2PingFrame) msg; | ||
if (frame.ack() && frame.content() == lastSentPingData) { | ||
lastReceivedPingTime = System.nanoTime(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a Ping ACK frame is received, the last received time is updated.
@Override | ||
public void cancel() { | ||
if (pingScheduler != null) { | ||
pingScheduler.cancel(false); | ||
pingScheduler = null; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scheduler is canceled when the channel becomes inactive or an exception occurs.
if (isOutOfTimeRange()) { | ||
countPingDrop(); | ||
|
||
if (isExceedAckDropThreshold()) { | ||
if (log.isInfoEnabled()) { | ||
log.info("Closing the channel due to delayed ping frame response (timeout: {} ns). {}", pingAckTimeoutNanos, channel); | ||
} | ||
|
||
close(); | ||
return; | ||
} | ||
|
||
if (log.isInfoEnabled()) { | ||
log.info("Dropping ping ACK frame in channel (ping data: {}). channel: {}", lastSentPingData, channel); | ||
} | ||
|
||
writePing(ctx); | ||
pingScheduler = invokeNextSchedule(); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the Ping ACK is not received within pingAckTimeoutNanos, but the retry count has not yet reached the pingAckDropThreshold, a retry is attempted.
isPingAckPending = false; | ||
pingAckDropCount = 0; | ||
pingScheduler = invokeNextSchedule(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the Ping ACK is not received within pingAckTimeoutNanos and retries have reached the pingAckDropThreshold,
the connection is considered invalid and the channel is closed.
IdleTimeoutHandler.addIdleTimeoutServerHandler( | ||
p, | ||
idleTimeout, | ||
new HttpConnectionImmediateClose() | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When upgrading from HTTP/1.1
to H2C
, the IdleTimeoutHandler
in H2CleartextCodec
is also modified.
Therefore, when starting with the HTTP/1.1
protocol, the IdleTimeoutHandler
is initially set to HttpConnectionImmediateClose
.
IdleTimeoutHandler.addIdleTimeoutServerHandler( | ||
p, | ||
idleTimeout, | ||
new HttpConnectionImmediateClose() | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For HTTP/1.1
, an IdleTimeoutHandler
is added as HttpConnectionImmediateClose
.
if (idleTimeout != null) { | ||
IdleTimeoutHandler.removeIdleTimeoutHandler(pipeline); | ||
IdleTimeoutHandler.addIdleTimeoutServerHandler( | ||
pipeline, | ||
idleTimeout, | ||
new Http2ConnectionLiveness( | ||
upgrader.http2FrameCodec, | ||
http2SettingsSpec != null ? http2SettingsSpec.pingAckTimeout() : null, | ||
http2SettingsSpec != null ? http2SettingsSpec.pingScheduleInterval() : null, | ||
http2SettingsSpec != null ? http2SettingsSpec.pingAckDropThreshold() : null | ||
) | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In H2CleartextCodec
, when upgrading from HTTP/1.1
to the H2C
protocol, the IdleTimeoutHandler
is changed to be based on Http2ConnectionLiveness
.
// When the server is configured with HTTP/1.1 and H2 and HTTP/1.1 is negotiated, | ||
// when channelActive event happens, this HttpTrafficHandler is still not in the pipeline, | ||
// and will not be able to add IdleTimeoutHandler. So in this use case add IdleTimeoutHandler here. | ||
IdleTimeoutHandler.addIdleTimeoutHandler(ctx.pipeline(), idleTimeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to be performed in configureHttp11OrH2CleartextPipeline()
.
@@ -546,7 +539,6 @@ void handleLastHttpContent(Object msg, ChannelPromise promise) { | |||
ctx.executor().execute(this); | |||
} | |||
else { | |||
IdleTimeoutHandler.addIdleTimeoutHandler(ctx.pipeline(), idleTimeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified to add the IdleTimeoutHandler
for each protocol in HttpServerConfig instead of handling it in HttpTrafficHandler.
Signed-off-by: raccoonback <[email protected]>
Description
In some cases, HTTP/2 connections may remain open even when they are no longer functional. By introducing a periodic Ping frame health check, we can proactively detect and close unhealthy connections.
The Http2ConnectionLivenessHandler schedules and sends Ping frames at a user-defined interval to verify the connection's liveness. If an acknowledgment (ACK) is not received within the specified time, the connection is considered unhealthy and is closed automatically.
However, if other frames are actively being sent or received, the scheduler does not send a Ping frame. This is because the server may delay ACK responses for various reasons. To prevent unnecessary connection termination, Ping frames are only sent when no read or write activity is detected.
Additionally, a configurable retry threshold for Ping frame transmission has been introduced. If a Ping frame fails to receive an ACK response, it will be retried up to the specified threshold before considering the connection unhealthy. This allows fine-tuning of the failure detection mechanism, balancing between aggressive failure detection and avoiding premature disconnections.
Scheduler Flow
Ping ACK Flow
Key Changes
Http2ConnectionLivenessHandler
handler to check connection health with Ping frames at a configurable interval.Http2SettingsSpec
.Related Issue