Skip to content

Commit 4353267

Browse files
committed
Merge #3980 into 1.3.0
Signed-off-by: Violeta Georgieva <[email protected]>
2 parents 19e328f + a4c7098 commit 4353267

File tree

19 files changed

+1561
-106
lines changed

19 files changed

+1561
-106
lines changed

docs/modules/ROOT/pages/http-client.adoc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,41 @@ include::{examples-dir}/channeloptions/Application.java[lines=18..54]
680680
<4> Configures the time between individual `keepalive` probes to 1 minute.
681681
<5> Configures the maximum number of TCP `keepalive` probes to 8.
682682

683+
=== HTTP/2 Connection Health Check
684+
For `HTTP/2 connections`, it's recommended to configure `PING` frames for connection maintenance and health monitoring.
685+
The `HttpClient` uses `PING` frames to detect unresponsive connections when `maxIdleTime` on the connection pool is configured.
686+
687+
Benefits of using `PING` frames:
688+
689+
- Actively probes connection liveness when idle
690+
- Detects unresponsive connections through timely health checks
691+
- Closes stale connections automatically
692+
- Maintains reliable connection pool state
693+
694+
[NOTE]
695+
====
696+
`HTTP/2` `PING` frame-based health checks require `maxIdleTime` on the connection pool to be configured as a prerequisite.
697+
Without idle timeout configuration, PING frames will not be sent and connections may remain open indefinitely,
698+
preventing proper detection of inactive or unresponsive connections.
699+
700+
The idle timeout and PING settings work together:
701+
702+
1. Connection becomes idle (no activity for `maxIdleTime`)
703+
2. A PING frame is sent to check if the peer is responsive
704+
3. If no ACK within `pingAckTimeout`, retry based on `pingAckDropThreshold`
705+
4. If all attempts fail, the connection is closed
706+
====
707+
708+
To enable `PING` frames for `HTTP/2` connections, configure the `HttpClient` as follows:
709+
710+
{examples-link}/liveness/Application.java
711+
----
712+
include::{examples-dir}/liveness/Application.java[lines=27..52]
713+
----
714+
<1> Configure `maxIdleTime` to enable PING-based health checks. When a connection is idle for this duration, a PING frame will be sent.
715+
<2> Configure `pingAckTimeout` - the maximum time to wait for each PING ACK response. If no ACK is received within this timeout, another PING is sent (up to the threshold).
716+
<3> Configure `pingAckDropThreshold` - the maximum number of PING frames to send before closing the connection. For example, `2` means send up to 2 PING frames (if first fails, try once more) before closing.
717+
683718
[[ssl-tls-timeout]]
684719
==== SSL/TLS Timeout
685720
`HttpClient` supports the SSL/TLS functionality provided by Netty.

docs/modules/ROOT/pages/http-server.adoc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,41 @@ include::{examples-dir}/idle/timeout/Application.java[lines=18..35]
789789
----
790790
<1> Configures the default idle timeout to 1 second.
791791

792+
=== HTTP/2 Connection Health Check
793+
For `HTTP/2 connections`, it's recommended to configure `PING` frames for connection maintenance and health monitoring.
794+
The `HttpServer` uses `PING` frames to detect unresponsive clients when `idleTimeout` is configured.
795+
796+
Benefits of using `PING` frames:
797+
798+
- Actively probes connection liveness when idle
799+
- Detects unresponsive clients through timely health checks
800+
- Closes stale connections automatically
801+
- Prevents resource exhaustion from dead connections
802+
803+
[NOTE]
804+
====
805+
`HTTP/2` `PING` frame-based health checks require `idleTimeout` to be configured as a prerequisite.
806+
Without idle timeout configuration, PING frames will not be sent and connections may remain open indefinitely,
807+
preventing proper detection of inactive or unresponsive connections.
808+
809+
The idle timeout and PING settings work together:
810+
811+
1. Connection becomes idle (no activity for `idleTimeout`)
812+
2. A PING frame is sent to check if the client is responsive
813+
3. If no ACK within `pingAckTimeout`, retry based on `pingAckDropThreshold`
814+
4. If all attempts fail, the connection is closed
815+
====
816+
817+
To enable `PING` frames for `HTTP/2` connections, configure the `HttpServer` as follows:
818+
819+
{examples-link}/liveness/Application.java
820+
----
821+
include::{examples-dir}/liveness/Application.java[lines=26..50]
822+
----
823+
<1> Configure `idleTimeout` to enable PING-based health checks. When a connection is idle for this duration, a PING frame will be sent.
824+
<2> Configure `pingAckTimeout` - the maximum time to wait for each PING ACK response. If no ACK is received within this timeout, another PING is sent (up to the threshold).
825+
<3> Configure `pingAckDropThreshold` - the maximum number of PING frames to send before closing the connection. For example, `2` means send up to 2 PING frames (if first fails, try once more) before closing.
826+
792827
[[http-server-ssl-tls-timeout]]
793828
=== SSL/TLS Timeout
794829
`HttpServer` supports the SSL/TLS functionality provided by Netty.

reactor-netty-core/src/main/java/reactor/netty/resources/PooledConnectionProvider.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,10 @@ PoolBuilder<T, PoolConfig<T>> newPoolInternal(
651651
return allocationStrategy;
652652
}
653653

654+
public @Nullable BiPredicate<Connection, ConnectionMetadata> evictionPredicate() {
655+
return evictionPredicate;
656+
}
657+
654658
public long maxIdleTime() {
655659
return this.maxIdleTime;
656660
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package reactor.netty.examples.documentation.http.client.liveness;
17+
18+
import io.netty.handler.codec.http.HttpHeaders;
19+
import reactor.core.publisher.Mono;
20+
import reactor.netty.http.HttpProtocol;
21+
import reactor.netty.http.client.HttpClient;
22+
import reactor.netty.resources.ConnectionProvider;
23+
import reactor.util.function.Tuple2;
24+
25+
import java.time.Duration;
26+
27+
public class Application {
28+
29+
public static void main(String[] args) {
30+
ConnectionProvider provider =
31+
ConnectionProvider.builder("liveness")
32+
.maxIdleTime(Duration.ofSeconds(2)) //<1>
33+
.build();
34+
35+
HttpClient client =
36+
HttpClient.create(provider)
37+
.protocol(HttpProtocol.H2)
38+
.secure()
39+
.http2Settings(builder -> builder.pingAckTimeout(Duration.ofMillis(600)) //<2>
40+
.pingAckDropThreshold(2)); //<3>
41+
42+
Tuple2<String, HttpHeaders> response =
43+
client.get()
44+
.uri("https://example.com/")
45+
.responseSingle((res, bytes) -> bytes.asString()
46+
.zipWith(Mono.just(res.responseHeaders())))
47+
.block();
48+
49+
System.out.println("Used stream ID: " + response.getT2().get("x-http2-stream-id"));
50+
System.out.println("Response: " + response.getT1());
51+
}
52+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package reactor.netty.examples.documentation.http.server.liveness;
17+
18+
import reactor.netty.DisposableServer;
19+
import reactor.netty.http.Http2SslContextSpec;
20+
import reactor.netty.http.HttpProtocol;
21+
import reactor.netty.http.server.HttpServer;
22+
23+
import java.io.File;
24+
import java.time.Duration;
25+
26+
public class Application {
27+
28+
public static void main(String[] args) {
29+
File cert = new File("certificate.crt");
30+
File key = new File("private.key");
31+
32+
Http2SslContextSpec http2SslContextSpec = Http2SslContextSpec.forServer(cert, key);
33+
34+
DisposableServer server =
35+
HttpServer.create()
36+
.port(8080)
37+
.protocol(HttpProtocol.H2)
38+
.secure(spec -> spec.sslContext(http2SslContextSpec))
39+
.idleTimeout(Duration.ofSeconds(1)) //<1>
40+
.http2Settings(builder -> builder.pingAckTimeout(Duration.ofMillis(600)) //<2>
41+
.pingAckDropThreshold(2)) //<3>
42+
.bindNow();
43+
44+
server.onDispose()
45+
.block();
46+
}
47+
}

reactor-netty-http/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ dependencies {
137137
testImplementation "io.specto:hoverfly-java-junit5:$hoverflyJavaVersion"
138138
testImplementation "org.apache.tomcat.embed:tomcat-embed-core:$tomcatVersion"
139139
testImplementation "io.projectreactor:reactor-test:$testAddonVersion"
140+
testImplementation "io.projectreactor.addons:reactor-extra:$reactorAddonsVersion"
140141
testImplementation "org.assertj:assertj-core:$assertJVersion"
141142
testImplementation "org.awaitility:awaitility:$awaitilityVersion"
142143
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"

0 commit comments

Comments
 (0)