-
Notifications
You must be signed in to change notification settings - Fork 657
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
* @param pingAckTimeout the interval in between consecutive PING frames | ||
* and ACK responses. Must be a positive value. | ||
*/ | ||
default Builder pingAckTimeout(Duration pingAckTimeout) { |
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.
Sets the interval for sending HTTP/2 PING frames and receiving ACK responses.
default Builder pingScheduleInterval(Duration pingScheduleInterval) { | ||
return this; | ||
} |
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.
Sets the execution interval for the scheduler that sends HTTP/2 PING frames
default Builder pingAckDropThreshold(Integer pingAckDropThreshold) { | ||
return this; | ||
} |
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.
Sets the threshold for retrying HTTP/2 PING frame transmissions. (default. 0)
/** | ||
* Sets the interval for sending HTTP/2 PING frames and receiving ACK responses. | ||
* | ||
* <p> | ||
* This method configures the time interval at which PING frames are sent to the peer. | ||
* The interval should be chosen carefully to balance between detecting connection issues | ||
* and minimizing unnecessary network traffic. | ||
* </p> | ||
* | ||
* <p> | ||
* If the interval is set too short, it may cause excessive network overhead. | ||
* If set too long, connection failures may not be detected in a timely manner. | ||
* </p> | ||
* | ||
* @param pingAckTimeout the interval in between consecutive PING frames | ||
* and ACK responses. Must be a positive value. | ||
*/ | ||
default Builder pingAckTimeout(Duration pingAckTimeout) { | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the execution interval for the scheduler that sends HTTP/2 PING frames | ||
* and periodically checks for ACK responses. | ||
* | ||
* <p> | ||
* This method configures the time interval at which the scheduler runs | ||
* to send PING frames and verify if ACK responses are received within | ||
* the expected timeframe. | ||
* Proper tuning of this interval helps in detecting connection issues | ||
* while avoiding unnecessary network overhead. | ||
* </p> | ||
* | ||
* <p> | ||
* If the interval is too short, it may increase network and CPU usage. | ||
* Conversely, setting it too long may delay the detection of connection failures. | ||
* </p> | ||
* | ||
* @param pingScheduleInterval the interval in at which the scheduler executes. | ||
* Must be a positive value. | ||
*/ | ||
default Builder pingScheduleInterval(Duration pingScheduleInterval) { | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the threshold for retrying HTTP/2 PING frame transmissions. | ||
* | ||
* <p> | ||
* This method defines the maximum number of attempts to send a PING frame | ||
* before considering the connection as unresponsive. | ||
* If the threshold is exceeded without receiving an ACK response, | ||
* the connection may be closed or marked as unhealthy. | ||
* </p> | ||
* | ||
* <p> | ||
* A lower threshold can detect connection failures more quickly but may lead | ||
* to premature disconnections. Conversely, a higher threshold allows more retries | ||
* but may delay failure detection. | ||
* </p> | ||
* | ||
* <p> | ||
* If this value is not specified, it defaults to 0, meaning only one attempt to send a PING frame is made without retries. | ||
* </p> | ||
* | ||
* @param pingAckDropThreshold the maximum number of PING transmission attempts. | ||
* Must be a positive integer. | ||
* The default value is 0, meaning no retries will occur and only one PING frame will be sent. | ||
*/ | ||
default Builder pingAckDropThreshold(Integer pingAckDropThreshold) { | ||
return this; | ||
} |
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.
It is declared as a default method for backward compatibility.
private final long pingAckTimeoutNanos; | ||
private final long pingScheduleIntervalNanos; | ||
private final int pingAckDropThreshold; |
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.
This is the configuration value provided by the user.
private boolean isOutOfTimeRange() { | ||
return pingAckTimeoutNanos < Math.abs(lastReceivedPingTime - lastSendingPingTime); | ||
} |
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.
It determines whether the Ping ACK was received within pingAckTimeoutNanos
.
private class PingWriteListener implements ChannelFutureListener { | ||
|
||
@Override | ||
public void operationComplete(ChannelFuture future) throws Exception { | ||
if (future.isSuccess()) { | ||
if (log.isDebugEnabled()) { | ||
log.debug("Wrote PING frame to {} channel.", future.channel()); | ||
} | ||
|
||
isPingAckPending = true; | ||
lastSendingPingTime = System.nanoTime(); | ||
} | ||
else if (log.isDebugEnabled()) { | ||
log.debug("Failed to wrote PING frame to {} channel.", future.channel()); | ||
} | ||
} | ||
} |
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 new Ping frame is successfully sent, the status is changed to 'ping awaiting', and lastSendingPingTime
is updated to the current time.
.addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.H2LivenessHandler, | ||
new Http2ConnectionLivenessHandler(codec.encoder(), pingAckTimeout, pingScheduleInterval, pingAckDropThreshold)) |
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.
configure Http2ConnectionLivenessHandler
.
REGISTRY.clear(); | ||
|
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.
Although not related to the purpose of this PR, handling has been implemented for failing tests.
To prevent interference with other tests, the metrics
are initialized.
Mockito.verify(spyLogger, times(0)) | ||
.warn(Mockito.eq("Connection pool creation limit exceeded: {} pools created, maximum expected is {}"), | ||
Mockito.eq(2), Mockito.eq(1)); |
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.
Although not related to the purpose of this PR, handling has been implemented for failing tests.
In PooledConnectionProvider
, the connectionPoolCount
is handled atomically to ensure that the log warning log.warn("Connection pool creation limit exceeded: {} pools created, maximum expected is {}")
is not called multiple times.
As a result, the test outcomes have been adjusted.
@raccoonback Can you rebase against branch |
Signed-off-by: raccoonback <kosb15@naver.com>
… in HTTP/2 Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
…e ping interval - ref. https://github.com/reactor/reactor-netty/actions/runs/13095356929/job/36537031690?pr=3612 Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
- 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 <kosb15@naver.com>
231b27d
to
a06c842
Compare
@violetagg |
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