Skip to content

Commit

Permalink
Issue 3582 configure Netty shutdown quiet period and shutdown timeout (
Browse files Browse the repository at this point in the history
…micronaut-projects#4319)

* Issue 3582 - adding feature to configure the shutdown quiet period and shutdown timeout for Netty event loop groups and http server

* Issue 3582 - adding feature to configure the shutdown quiet period and shutdown timeout for Netty event loop groups and http server

* Issue 3582
- adding feature to configure the shutdown quiet period and shutdown timeouts
- adding overloaded versions of HttpClient create; RxStreamingHttpClient create; and RxHttpClientFactory createClient and createStreamingClient to accept a HttpClientConfiguration instance, for cases where the client is created outside the Micronaut application context

* Issue 3582
- adding feature to configure the shutdown quiet period and shutdown timeouts
- adding overloaded versions of HttpClient create; RxStreamingHttpClient create; and RxHttpClientFactory createClient and createStreamingClient to accept a HttpClientConfiguration instance, for cases where the client is created outside the Micronaut application context

* Issue 3582
- revise section 6.27.1 of the documentaion to add clarification about Netty server configuration
- add deprecation warning in NettyHttpServer about preferring Netty event loop groups configurations

* Update threadPools.adoc

* Issue 3582
- adding feature to configure the shutdown quiet period and shutdown timeouts
- adding overloaded versions of HttpClient create; RxStreamingHttpClient create; and RxHttpClientFactory createClient and createStreamingClient to accept a HttpClientConfiguration instance, for cases where the client is created outside the Micronaut application context

Co-authored-by: James Kleeh <james.kleeh@gmail.com>
  • Loading branch information
wetted and jameskleeh authored Nov 11, 2020
1 parent ec029fc commit 937a86e
Show file tree
Hide file tree
Showing 16 changed files with 372 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,17 @@ default HttpClient refresh() {
static HttpClient create(@Nullable URL url) {
return HttpClientConfiguration.createClient(url);
}

/**
* Create a new {@link HttpClient} with the specified configuration. Note that this method should only be used
* outside of the context of an application. Within Micronaut use {@link javax.inject.Inject} to inject a client instead
*
* @param url The base URL
* @param configuration the client configuration
* @return The client
* @since 2.2.0
*/
static HttpClient create(@Nullable URL url, HttpClientConfiguration configuration) {
return HttpClientConfiguration.createClient(url, configuration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
import java.util.concurrent.ThreadFactory;

/**
* Configuration for the {@link HttpClient}.
* Configuration for the {@link HttpClient}. This configuration only takes affect for {@link HttpClient}
* instances created outside the application context using {@link HttpClient#create(URL, HttpClientConfiguration)}.
* For clients created within the context using, e.g. {@link javax.inject.Inject} or
* {@link io.micronaut.context.ApplicationContext#createBean(Class)}, use event loop group configuration.
*
* @author Graeme Rocher
* @since 1.0
Expand All @@ -55,6 +58,12 @@ public abstract class HttpClientConfiguration {
@SuppressWarnings("WeakerAccess")
public static final long DEFAULT_READ_IDLE_TIMEOUT_MINUTES = 5;

/**
* The default shutdown timeout in millis.
*/
@SuppressWarnings("WeakerAccess")
public static final long DEFAULT_SHUTDOWN_QUIET_PERIOD_MILLISECONDS = 1;

/**
* The default shutdown timeout in millis.
*/
Expand Down Expand Up @@ -98,6 +107,8 @@ public abstract class HttpClientConfiguration {

private Duration readIdleTimeout = Duration.of(DEFAULT_READ_IDLE_TIMEOUT_MINUTES, ChronoUnit.MINUTES);

private Duration shutdownQuietPeriod = Duration.ofMillis(DEFAULT_SHUTDOWN_QUIET_PERIOD_MILLISECONDS);

private Duration shutdownTimeout = Duration.ofMillis(DEFAULT_SHUTDOWN_TIMEOUT_MILLISECONDS);

private int maxContentLength = DEFAULT_MAX_CONTENT_LENGTH;
Expand Down Expand Up @@ -158,6 +169,7 @@ public HttpClientConfiguration(HttpClientConfiguration copy) {
this.exceptionOnErrorStatus = copy.exceptionOnErrorStatus;
this.eventLoopGroup = copy.eventLoopGroup;
this.followRedirects = copy.followRedirects;
this.logLevel = copy.logLevel;
this.loggerName = copy.loggerName;
this.maxContentLength = copy.maxContentLength;
this.proxyAddress = copy.proxyAddress;
Expand All @@ -168,6 +180,7 @@ public HttpClientConfiguration(HttpClientConfiguration copy) {
this.readIdleTimeout = copy.readIdleTimeout;
this.readTimeout = copy.readTimeout;
this.shutdownTimeout = copy.shutdownTimeout;
this.shutdownQuietPeriod = copy.shutdownQuietPeriod;
this.sslConfiguration = copy.sslConfiguration;
this.threadFactory = copy.threadFactory;
this.httpVersion = copy.httpVersion;
Expand Down Expand Up @@ -355,6 +368,15 @@ public Optional<Duration> getConnectTtl() {
return Optional.ofNullable(connectTtl);
}

/**
* The amount of quiet period for shutdown.
*
* @return The shutdown timeout
*/
public Optional<Duration> getShutdownQuietPeriod() {
return Optional.ofNullable(shutdownQuietPeriod);
}

/**
* The amount of time to wait for shutdown.
*
Expand All @@ -364,6 +386,17 @@ public Optional<Duration> getShutdownTimeout() {
return Optional.ofNullable(shutdownTimeout);
}

/**
* Sets the amount of quiet period for shutdown of client thread pools. Default value ({@value io.micronaut.http.client.HttpClientConfiguration#DEFAULT_SHUTDOWN_QUIET_PERIOD_MILLISECONDS} milliseconds).
*
* If a task is submitted during the quiet period, it will be accepted and the quiet period will start over.
*
* @param shutdownQuietPeriod The shutdown quiet period
*/
public void setShutdownQuietPeriod(@Nullable Duration shutdownQuietPeriod) {
this.shutdownQuietPeriod = shutdownQuietPeriod;
}

/**
* Sets the amount of time to wait for shutdown of client thread pools. Default value ({@value io.micronaut.http.client.HttpClientConfiguration#DEFAULT_SHUTDOWN_TIMEOUT_MILLISECONDS} milliseconds).
*
Expand Down Expand Up @@ -586,19 +619,27 @@ public Proxy resolveProxy(boolean isSsl, String host, int port) {
*/
@Internal
static RxHttpClient createClient(@Nullable URL url) {
RxHttpClientFactory clientFactory = HttpClientConfiguration.clientFactory;
if (clientFactory == null) {
synchronized (HttpClientConfiguration.class) { // double check
clientFactory = HttpClientConfiguration.clientFactory;
if (clientFactory == null) {
clientFactory = resolveClientFactory();
HttpClientConfiguration.clientFactory = clientFactory;
}
}
}
RxHttpClientFactory clientFactory = getRxHttpClientFactory();

return clientFactory.createClient(url);
}

/**
* Create a new {@link HttpClient} with the specified configuration. Note that this method should only be used
* outside of the context of an application. Within Micronaut use {@link javax.inject.Inject} to inject a client instead
*
* @param url The base URL
* @param configuration the client configuration
* @return The client
* @since 2.2.0
*/
@Internal
static RxHttpClient createClient(@Nullable URL url, HttpClientConfiguration configuration) {
RxHttpClientFactory clientFactory = getRxHttpClientFactory();

return clientFactory.createClient(url, configuration);
}

/**
* Create a new {@link HttpClient}. Note that this method should only be used outside of the context of an application. Within Micronaut use
* {@link javax.inject.Inject} to inject a client instead
Expand All @@ -609,6 +650,27 @@ static RxHttpClient createClient(@Nullable URL url) {
@Internal
static RxStreamingHttpClient createStreamingClient(@NonNull URL url) {
ArgumentUtils.requireNonNull("url", url);
RxHttpClientFactory clientFactory = getRxHttpClientFactory();
return clientFactory.createStreamingClient(url);
}

/**
* Create a new {@link HttpClient} with the specified configuration. Note that this method should only be used
* outside of the context of an application. Within Micronaut use {@link javax.inject.Inject} to inject a client instead
*
* @param url The base URL
* @param configuration The client configuration
* @return The client
* @since 2.2.0
*/
@Internal
static RxStreamingHttpClient createStreamingClient(@NonNull URL url, HttpClientConfiguration configuration) {
ArgumentUtils.requireNonNull("url", url);
RxHttpClientFactory clientFactory = getRxHttpClientFactory();
return clientFactory.createStreamingClient(url, configuration);
}

private static RxHttpClientFactory getRxHttpClientFactory() {
RxHttpClientFactory clientFactory = HttpClientConfiguration.clientFactory;
if (clientFactory == null) {
synchronized (HttpClientConfiguration.class) { // double check
Expand All @@ -619,7 +681,7 @@ static RxStreamingHttpClient createStreamingClient(@NonNull URL url) {
}
}
}
return clientFactory.createStreamingClient(url);
return clientFactory;
}

private static RxHttpClientFactory resolveClientFactory() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ public interface RxHttpClientFactory {
*/
RxHttpClient createClient(@Nullable URL url);

/**
* Create a new {@link HttpClient} with the specified configuration. Note that this method should only be used
* outside of the context of an application. Within Micronaut use {@link javax.inject.Inject} to inject a client instead
*
* @param url The base URL
* @param configuration the client configuration
* @return The client
* @since 2.2.0
*/
RxHttpClient createClient(@Nullable URL url, HttpClientConfiguration configuration);

/**
* Create a new {@link HttpClient}. Note that this method should only be used outside of the context of an application. Within Micronaut use
* {@link javax.inject.Inject} to inject a client instead
Expand All @@ -43,4 +54,15 @@ public interface RxHttpClientFactory {
* @return The client
*/
RxStreamingHttpClient createStreamingClient(@Nullable URL url);

/**
* Create a new {@link HttpClient} with the specified configuration. Note that this method should only be used
* outside of the context of an application. Within Micronaut use {@link javax.inject.Inject} to inject a client instead
*
* @param url The base URL
* @param configuration The client configuration
* @return The client
* @since 2.2.0
*/
RxStreamingHttpClient createStreamingClient(@Nullable URL url, HttpClientConfiguration configuration);
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,17 @@ default <I, O> Flowable<O> jsonStream(HttpRequest<I> request, Class<O> type) {
static RxStreamingHttpClient create(URL url) {
return HttpClientConfiguration.createStreamingClient(url);
}

/**
* Create a new {@link HttpClient} with the specified configuration. Note that this method should only be used
* outside of the context of an application. Within Micronaut use {@link javax.inject.Inject} to inject a client instead
*
* @param url The base URL
* @param configuration The client configuration
* @return The client
* @since 2.2.0
*/
static RxStreamingHttpClient create(URL url, HttpClientConfiguration configuration) {
return HttpClientConfiguration.createStreamingClient(url, configuration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@
import java.util.function.Consumer;
import java.util.function.Supplier;

import static io.micronaut.http.client.HttpClientConfiguration.DEFAULT_SHUTDOWN_QUIET_PERIOD_MILLISECONDS;
import static io.micronaut.http.client.HttpClientConfiguration.DEFAULT_SHUTDOWN_TIMEOUT_MILLISECONDS;

/**
* Default implementation of the {@link HttpClient} interface based on Netty.
*
Expand Down Expand Up @@ -450,10 +453,13 @@ public HttpClient stop() {
}
}
if (shutdownGroup) {
Duration shutdownTimeout = configuration.getShutdownTimeout().orElse(Duration.ofMillis(100));
Duration shutdownTimeout = configuration.getShutdownTimeout()
.orElse(Duration.ofMillis(DEFAULT_SHUTDOWN_TIMEOUT_MILLISECONDS));
Duration shutdownQuietPeriod = configuration.getShutdownQuietPeriod()
.orElse(Duration.ofMillis(DEFAULT_SHUTDOWN_QUIET_PERIOD_MILLISECONDS));

Future<?> future = this.group.shutdownGracefully(
1,
shutdownQuietPeriod.toMillis(),
shutdownTimeout.toMillis(),
TimeUnit.MILLISECONDS
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.micronaut.http.client.netty;

import io.micronaut.core.annotation.Internal;
import io.micronaut.http.client.HttpClientConfiguration;
import io.micronaut.http.client.RxHttpClient;
import io.micronaut.http.client.RxHttpClientFactory;
import io.micronaut.http.client.RxStreamingHttpClient;
Expand All @@ -35,8 +36,18 @@ public RxHttpClient createClient(URL url) {
return new DefaultHttpClient(url);
}

@Override
public RxHttpClient createClient(URL url, HttpClientConfiguration configuration) {
return new DefaultHttpClient(url, configuration);
}

@Override
public RxStreamingHttpClient createStreamingClient(URL url) {
return new DefaultHttpClient(url);
}

@Override
public RxStreamingHttpClient createStreamingClient(URL url, HttpClientConfiguration configuration) {
return new DefaultHttpClient(url, configuration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class DefaultHttpClientConfigurationSpec extends Specification {
'read-idle-timeout' | 'readIdleTimeout' | '-1' | Optional.empty()
'connect-ttl' | 'connectTtl' | '1s' | Optional.of(Duration.ofSeconds(1))
'exception-on-error-status' | 'exceptionOnErrorStatus' | 'false' | false
'shutdown-quiet-period' | 'shutdownQuietPeriod' | '1ms' | Optional.of(Duration.ofMillis(1))
'shutdown-quiet-period' | 'shutdownQuietPeriod' | '2s' | Optional.of(Duration.ofSeconds(2))
'shutdown-timeout' | 'shutdownTimeout' | '100ms' | Optional.of(Duration.ofMillis(100))
'shutdown-timeout' | 'shutdownTimeout' | '15s' | Optional.of(Duration.ofSeconds(15))
}


Expand Down
2 changes: 2 additions & 0 deletions http-netty/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ dependencies {
api dependencyModuleVersion("netty", "netty-codec-http2")

implementation dependencyVersion("rxjava2")

testImplementation project(":runtime")
}

spotless {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.micronaut.core.bind.annotation.Bindable;
import io.micronaut.core.util.StringUtils;

import java.time.Duration;
import java.util.Optional;

/**
Expand All @@ -39,6 +40,8 @@ public class DefaultEventLoopGroupConfiguration implements EventLoopGroupConfigu
private final boolean preferNativeTransport;
private final String name;
private final String executor;
private final Duration shutdownQuietPeriod;
private final Duration shutdownTimeout;

/**
* Default constructor.
Expand All @@ -48,19 +51,28 @@ public class DefaultEventLoopGroupConfiguration implements EventLoopGroupConfigu
* @param ioRatio The IO ratio (optional)
* @param preferNativeTransport Whether native transport is to be preferred
* @param executor A named executor service to use (optional)
* @param shutdownQuietPeriod The shutdown quiet period
* @param shutdownTimeout The shutdown timeout (must be >= shutdownQuietPeriod)
*/
@ConfigurationInject
public DefaultEventLoopGroupConfiguration(
@Parameter String name,
@Bindable(defaultValue = "0") int numThreads,
@Nullable Integer ioRatio,
@Bindable(defaultValue = StringUtils.FALSE) boolean preferNativeTransport,
@Nullable String executor) {
@Nullable String executor,
@Nullable Duration shutdownQuietPeriod,
@Nullable Duration shutdownTimeout
) {
this.name = name;
this.numThreads = numThreads;
this.ioRatio = ioRatio;
this.preferNativeTransport = preferNativeTransport;
this.executor = executor;
this.shutdownQuietPeriod = Optional.ofNullable(shutdownQuietPeriod)
.orElse(Duration.ofSeconds(DEFAULT_SHUTDOWN_QUIET_PERIOD));
this.shutdownTimeout = Optional.ofNullable(shutdownTimeout)
.orElse(Duration.ofSeconds(DEFAULT_SHUTDOWN_TIMEOUT));
}

/**
Expand All @@ -72,6 +84,8 @@ public DefaultEventLoopGroupConfiguration() {
this.ioRatio = null;
this.preferNativeTransport = false;
this.executor = null;
this.shutdownQuietPeriod = Duration.ofSeconds(DEFAULT_SHUTDOWN_QUIET_PERIOD);
this.shutdownTimeout = Duration.ofSeconds(DEFAULT_SHUTDOWN_TIMEOUT);
}

/**
Expand Down Expand Up @@ -108,4 +122,14 @@ public boolean isPreferNativeTransport() {
public String getName() {
return name;
}

@Override
public Duration getShutdownQuietPeriod() {
return shutdownQuietPeriod;
}

@Override
public Duration getShutdownTimeout() {
return shutdownTimeout;
}
}
Loading

0 comments on commit 937a86e

Please sign in to comment.