Skip to content

Commit

Permalink
Add warmup functionality for the servers/clients (#1455)
Browse files Browse the repository at this point in the history
The warmup triggers an initialisation of the event loop groups, the host name resolver,
loads the necessary native libraries for the transport and native libraries
for the security if security is enabled.

HttpClient:
When the URL scheme is HTTPS and there is no security configured,
the default security will be used thus always try to load the OpenSsl natives
see HttpClientConnect.MonoHttpConnect#subscribe
The URL is parsed on request so the scheme is not known in advance
in order to use this information in the warmup.
With one and the same HttpClient you may trigger various requests

HttpClient client = HttpClient.create();
...
client.get().uri("https://abc.com/")
...
client.get().uri("http://qwe.com/")

Related to #560, #1023, #1425
  • Loading branch information
violetagg authored Jan 11, 2021
1 parent 639349a commit 3810e8c
Show file tree
Hide file tree
Showing 22 changed files with 653 additions and 5 deletions.
25 changes: 25 additions & 0 deletions docs/asciidoc/http-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ include::{examplesdir}/address/Application.java[lines=18..33]
<2> Configures the `HTTP` port
====

== Eager Initialization

By default, the initialization of the `HttpClient` resources happens on demand. This means that the `first
request` absorbs the extra time needed to initialize and load:

* the event loop group
* the host name resolver
* the native transport libraries (when native transport is used)
* the native libraries for the security (in case of `OpenSsl`)

When you need to preload these resources, you can configure the `HttpClient` as follows:

====
[source,java,indent=0]
.{examplesdir}/warmup/Application.java
----
include::{examplesdir}/warmup/Application.java[lines=18..36]
----
<1> Initialize and load the event loop group, the host name resolver, the native transport libraries and
the native libraries for the security
<2> Host name resolution happens with the first request.
In this example, a connection pool is used, so with the first request the connection to the URL is established,
the subsequent requests to the same URL reuse the connections from the pool.
====

== Writing Data

To send data to a given `HTTP` endpoint, you can provide a `Publisher` by using the
Expand Down
20 changes: 20 additions & 0 deletions docs/asciidoc/http-server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ include::{examplesdir}/address/Application.java[lines=18..33]
<2> Configures the `HTTP` server port
====

== Eager Initialization

By default, the initialization of the `HttpServer` resources happens on demand. This means that the `bind
operation` absorbs the extra time needed to initialize and load:

* the event loop groups
* the native transport libraries (when native transport is used)
* the native libraries for the security (in case of `OpenSsl`)

When you need to preload these resources, you can configure the `HttpServer` as follows:

====
[source,java,indent=0]
.{examplesdir}/warmup/Application.java
----
include::{examplesdir}/warmup/Application.java[lines=18..36]
----
<1> Initialize and load the event loop groups, the native transport libraries and the native libraries for the security
====

== Routing HTTP

Defining routes for the `HTTP` server requires configuring the provided
Expand Down
23 changes: 23 additions & 0 deletions docs/asciidoc/tcp-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ include::{examplesdir}/address/Application.java[lines=18..33]
<2> Configures the `TCP` port
====

== Eager Initialization

By default, the initialization of the `TcpClient` resources happens on demand. This means that the `connect
operation` absorbs the extra time needed to initialize and load:

* the event loop group
* the host name resolver
* the native transport libraries (when native transport is used)
* the native libraries for the security (in case of `OpenSsl`)

When you need to preload these resources, you can configure the `TcpClient` as follows:

====
[source,java,indent=0]
.{examplesdir}/warmup/Application.java
----
include::{examplesdir}/warmup/Application.java[lines=18..39]
----
<1> Initialize and load the event loop group, the host name resolver, the native transport libraries and
the native libraries for the security
<2> Host name resolution happens when connecting to the remote peer
====

== Writing Data

To send data to a given endpoint, you must attach an I/O handler.
Expand Down
20 changes: 20 additions & 0 deletions docs/asciidoc/tcp-server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ include::{examplesdir}/address/Application.java[lines=18..33]
<2> Configures the `TCP` server port
====

== Eager Initialization

By default, the initialization of the `TcpServer` resources happens on demand. This means that the `bind
operation` absorbs the extra time needed to initialize and load:

* the event loop groups
* the native transport libraries (when native transport is used)
* the native libraries for the security (in case of `OpenSsl`)

When you need to preload these resources, you can configure the `TcpServer` as follows:

====
[source,java,indent=0]
.{examplesdir}/warmup/Application.java
----
include::{examplesdir}/warmup/Application.java[lines=18..36]
----
<1> Initialize and load the event loop groups, the native transport libraries and the native libraries for the security
====

== Writing Data

In order to send data to a connected client, you must attach an I/O handler.
Expand Down
21 changes: 21 additions & 0 deletions docs/asciidoc/udp-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ include::{examplesdir}/address/Application.java[lines=18..34]
<2> Configures the `port` to which this client should connect
====

== Eager Initialization

By default, the initialization of the `UdpClient` resources happens on demand. This means that the `connect
operation` absorbs the extra time needed to initialize and load:

* the event loop group
* the host name resolver
* the native transport libraries (when native transport is used)

When you need to preload these resources, you can configure the `UdpClient` as follows:

====
[source,java,indent=0]
.{examplesdir}/warmup/Application.java
----
include::{examplesdir}/warmup/Application.java[lines=18..40]
----
<1> Initialize and load the event loop group, the host name resolver, and the native transport libraries
<2> Host name resolution happens when connecting to the remote peer
====

== Writing Data

To send data to a given peer, you must attach an I/O handler.
Expand Down
19 changes: 19 additions & 0 deletions docs/asciidoc/udp-server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ include::{examplesdir}/address/Application.java[lines=18..34]
<2> Configures the `UDP` server port
====

== Eager Initialization

By default, the initialization of the `UdpServer` resources happens on demand. This means that the `bind
operation` absorbs the extra time needed to initialize and load:

* the event loop group
* the native transport libraries (when native transport is used)

When you need to preload these resources, you can configure the `UdpServer` as follows:

====
[source,java,indent=0]
.{examplesdir}/warmup/Application.java
----
include::{examplesdir}/warmup/Application.java[lines=18..51]
----
<1> Initialize and load the event loop group and the native transport libraries
====

== Writing Data

To send data to the remote peer, you must attach an I/O handler.
Expand Down
4 changes: 4 additions & 0 deletions reactor-netty-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ dependencies {
}

// Testing

// JSR-305 annotations
testCompileOnly "com.google.code.findbugs:jsr305:$jsr305Version"

testCompile "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion"
testCompile "io.projectreactor:reactor-test:$testAddonVersion"
testCompile "org.assertj:assertj-core:$assertJVersion"
Expand Down
27 changes: 27 additions & 0 deletions reactor-netty-core/src/main/java/reactor/netty/tcp/TcpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.AttributeKey;
Expand Down Expand Up @@ -355,6 +357,31 @@ public TcpClient secure(SslProvider sslProvider) {
return dup;
}

/**
* Based on the actual configuration, returns a {@link Mono} that triggers:
* <ul>
* <li>an initialization of the event loop group</li>
* <li>an initialization of the host name resolver</li>
* <li>loads the necessary native libraries for the transport</li>
* <li>loads the necessary native libraries for the security if there is such</li>
* </ul>
* By default, when method is not used, the {@code connect operation} absorbs the extra time needed to load resources.
*
* @return a {@link Mono} representing the completion of the warmup
* @since 1.0.3
*/
@Override
public Mono<Void> warmup() {
return Mono.when(
super.warmup(),
Mono.fromRunnable(() -> {
SslProvider provider = configuration().sslProvider();
if (provider != null && !(provider.getSslContext() instanceof JdkSslContext)) {
OpenSsl.version();
}
}));
}

@Override
public TcpClient wiretap(boolean enable) {
return super.wiretap(enable);
Expand Down
26 changes: 26 additions & 0 deletions reactor-netty-core/src/main/java/reactor/netty/tcp/TcpServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.reactivestreams.Publisher;
Expand Down Expand Up @@ -219,6 +221,30 @@ public TcpServer secure(SslProvider sslProvider) {
return dup;
}

/**
* Based on the actual configuration, returns a {@link Mono} that triggers:
* <ul>
* <li>an initialization of the event loop groups</li>
* <li>loads the necessary native libraries for the transport</li>
* <li>loads the necessary native libraries for the security if there is such</li>
* </ul>
* By default, when method is not used, the {@code bind operation} absorbs the extra time needed to load resources.
*
* @return a {@link Mono} representing the completion of the warmup
* @since 1.0.3
*/
@Override
public Mono<Void> warmup() {
return Mono.when(
super.warmup(),
Mono.fromRunnable(() -> {
SslProvider provider = configuration().sslProvider();
if (provider != null && !(provider.getSslContext() instanceof JdkSslContext)) {
OpenSsl.version();
}
}));
}

@Override
public TcpServer wiretap(boolean enable) {
return super.wiretap(enable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,26 @@ public T runOn(LoopResources loopResources, boolean preferNative) {
}
return dup;
}

/**
* Based on the actual configuration, returns a {@link Mono} that triggers:
* <ul>
* <li>an initialization of the event loop group</li>
* <li>an initialization of the host name resolver</li>
* <li>loads the necessary native libraries for the transport</li>
* </ul>
* By default, when method is not used, the {@code connect operation} absorbs the extra time needed to initialize and
* load the resources.
*
* @return a {@link Mono} representing the completion of the warmup
* @since 1.0.3
*/
public Mono<Void> warmup() {
return Mono.fromRunnable(() -> {
configuration().eventLoopGroup();

// By default the host name resolver uses the event loop group configured on client level
configuration().resolverInternal();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,27 @@ public T port(int port) {
return bindAddress(() -> AddressUtils.updatePort(configuration().bindAddress(), port));
}

/**
* Based on the actual configuration, returns a {@link Mono} that triggers:
* <ul>
* <li>an initialization of the event loop groups</li>
* <li>loads the necessary native libraries for the transport</li>
* </ul>
* By default, when method is not used, the {@code bind operation} absorbs the extra time needed to load resources.
*
* @return a {@link Mono} representing the completion of the warmup
* @since 1.0.3
*/
public Mono<Void> warmup() {
return Mono.fromRunnable(() -> {
// event loop group for the server
configuration().childEventLoopGroup();

// event loop group for the server selector
configuration().eventLoopGroup();
});
}

static final Logger log = Loggers.getLogger(ServerTransport.class);

static class Acceptor extends ChannelInboundHandlerAdapter {
Expand Down
15 changes: 15 additions & 0 deletions reactor-netty-core/src/main/java/reactor/netty/udp/UdpServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,21 @@ public final UdpServer runOn(LoopResources loopResources, InternetProtocolFamily
return dup;
}

/**
* Based on the actual configuration, returns a {@link Mono} that triggers:
* <ul>
* <li>an initialization of the event loop group</li>
* <li>loads the necessary native libraries for the transport</li>
* </ul>
* By default, when method is not used, the {@code bind operation} absorbs the extra time needed to load resources.
*
* @return a {@link Mono} representing the completion of the warmup
* @since 1.0.3
*/
public final Mono<Void> warmup() {
return Mono.fromRunnable(() -> configuration().eventLoopGroup());
}

@Override
public final UdpServer wiretap(boolean enable) {
return super.wiretap(enable);
Expand Down
Loading

0 comments on commit 3810e8c

Please sign in to comment.