From 5b729ab1267406afaf07da07a7b6fc1ba89e2cef Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Thu, 17 Nov 2016 17:37:09 -0500 Subject: [PATCH] Add graceful shutdown support to undertow (cherry picked from commit 4728aacf6531cc99234e71f446a016bb676dbe90) --- .../autoconfigure/web/ServerProperties.java | 11 +++++++ .../appendix-application-properties.adoc | 1 + .../UndertowEmbeddedServletContainer.java | 29 +++++++++++++++++-- ...dertowEmbeddedServletContainerFactory.java | 13 +++++++-- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index b9661e230839..c53a3d013f72 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -1286,6 +1286,13 @@ public static class Undertow { */ private Boolean directBuffers; + /** + * Time in milliseconds that a shutdown will wait for in progress HTTP request to complete before + * closing all the connections. When not set it defaults to 30000 ms. Use a value of -1 to indicate no + * (i.e. infinite) timeout. + */ + private Long gracefulShutdownTimeout; + private final Accesslog accesslog = new Accesslog(); public Integer getBufferSize() { @@ -1350,6 +1357,10 @@ void customizeUndertow(final ServerProperties serverProperties, if (this.accesslog.enabled != null) { factory.setAccessLogEnabled(this.accesslog.enabled); } + if (this.gracefulShutdownTimeout != null) { + factory.setGracefulShutdownTimeout(this.gracefulShutdownTimeout); + } + factory.setAccessLogDirectory(this.accesslog.dir); factory.setAccessLogPattern(this.accesslog.pattern); factory.setAccessLogPrefix(this.accesslog.prefix); diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 50eab1f67699..792cd9b884c6 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -231,6 +231,7 @@ content into your application; rather pick only the properties that you need. server.undertow.direct-buffers= # Allocate buffers outside the Java heap. server.undertow.io-threads= # Number of I/O threads to create for the worker. server.undertow.worker-threads= # Number of worker threads. + server.undertow.graceful-shutdown-timeout= # Graceful shudown timeout in ms. # FREEMARKER ({sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[FreeMarkerAutoConfiguration]) spring.freemarker.allow-request-override=false # Set whether HttpServletRequest attributes are allowed to override (hide) controller generated model attributes of the same name. diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java index 171dd8a3012e..390350af12fc 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java @@ -33,6 +33,7 @@ import io.undertow.predicate.Predicates; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.GracefulShutdownHandler; import io.undertow.server.handlers.encoding.ContentEncodingRepository; import io.undertow.server.handlers.encoding.EncodingHandler; import io.undertow.server.handlers.encoding.GzipEncodingProvider; @@ -85,11 +86,14 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine private final Compression compression; private final String serverHeader; + private final long gracefulShutdownTimeout; private Undertow undertow; private boolean started = false; + private GracefulShutdownHandler shutdownHander; + /** * Create a new {@link UndertowEmbeddedServletContainer} instance. * @param builder the builder @@ -116,7 +120,7 @@ public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manag String contextPath, boolean useForwardHeaders, boolean autoStart, Compression compression) { this(builder, manager, contextPath, useForwardHeaders, autoStart, compression, - null); + null, 30000); } /** @@ -128,10 +132,11 @@ public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manag * @param autoStart if the server should be started * @param compression compression configuration * @param serverHeader string to be used in HTTP header + * @param gracefulShutdownTimeout shutdown timeout */ public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manager, String contextPath, boolean useForwardHeaders, boolean autoStart, - Compression compression, String serverHeader) { + Compression compression, String serverHeader, long gracefulShutdownTimeout) { this.builder = builder; this.manager = manager; this.contextPath = contextPath; @@ -139,6 +144,7 @@ public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manag this.autoStart = autoStart; this.compression = compression; this.serverHeader = serverHeader; + this.gracefulShutdownTimeout = gracefulShutdownTimeout; } @Override @@ -192,6 +198,16 @@ private Undertow createUndertowServer() throws ServletException { if (StringUtils.hasText(this.serverHeader)) { httpHandler = Handlers.header(httpHandler, "Server", this.serverHeader); } + + if (this.gracefulShutdownTimeout == 0) { + // don't need a shutdown handler if we are not going to wait for graceful shutdown. + this.shutdownHander = null; + } + else { + this.shutdownHander = new GracefulShutdownHandler(httpHandler); + httpHandler = this.shutdownHander; + } + this.builder.setHandler(httpHandler); return this.builder.build(); } @@ -308,6 +324,15 @@ public void stop() throws EmbeddedServletContainerException { if (this.started) { try { this.started = false; + if (this.shutdownHander != null) { + this.shutdownHander.shutdown(); + if (this.gracefulShutdownTimeout == -1) { + this.shutdownHander.awaitShutdown(); + } + else { + this.shutdownHander.awaitShutdown(this.gracefulShutdownTimeout); + } + } this.manager.stop(); this.undertow.stop(); } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java index 4eb94098e1df..510ff86dff1f 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java @@ -129,6 +129,7 @@ public class UndertowEmbeddedServletContainerFactory private boolean accessLogRotate = true; private boolean useForwardHeaders; + private long gracefulShutdownTimeout = 30000; /** * Create a new {@link UndertowEmbeddedServletContainerFactory} instance. @@ -529,7 +530,7 @@ private void configureMimeMappings(DeploymentInfo servletBuilder) { protected UndertowEmbeddedServletContainer getUndertowEmbeddedServletContainer( Builder builder, DeploymentManager manager, int port) { return new UndertowEmbeddedServletContainer(builder, manager, getContextPath(), - isUseForwardHeaders(), port >= 0, getCompression(), getServerHeader()); + isUseForwardHeaders(), port >= 0, getCompression(), getServerHeader(), getGracefulShutdownTimeout()); } @Override @@ -596,13 +597,21 @@ protected final boolean isUseForwardHeaders() { /** * Set if x-forward-* headers should be processed. - * @param useForwardHeaders if x-forward headers should be used + * @param useForwardHeaders if x-forward headers should be used * @since 1.3.0 */ public void setUseForwardHeaders(boolean useForwardHeaders) { this.useForwardHeaders = useForwardHeaders; } + public void setGracefulShutdownTimeout(long gracefulShutdownTimeout) { + this.gracefulShutdownTimeout = gracefulShutdownTimeout; + } + + public Long getGracefulShutdownTimeout() { + return this.gracefulShutdownTimeout; + } + /** * Undertow {@link ResourceManager} for JAR resources. */