Add support for gracefully shutting down the web server

This commit adds support for gracefully shutting down the embedded
web server. When a grace period is configured
(server.shutdown.grace-period), upon shutdown, the web server will no
longer permit new requests and will wait for up to the grace period
for active requests to complete.

Closes gh-4657
This commit is contained in:
Andy Wilkinson
2020-03-09 15:55:02 +00:00
parent 067accb3a8
commit 308e1d3675
40 changed files with 1541 additions and 56 deletions

View File

@@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.boot.web.server.Compression;
import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.servlet.server.Encoding;
import org.springframework.boot.web.servlet.server.Jsp;
@@ -114,6 +115,9 @@ public class ServerProperties {
@NestedConfigurationProperty
private final Http2 http2 = new Http2();
@NestedConfigurationProperty
private final Shutdown shutdown = new Shutdown();
private final Servlet servlet = new Servlet();
private final Tomcat tomcat = new Tomcat();
@@ -199,6 +203,10 @@ public class ServerProperties {
return this.http2;
}
public Shutdown getShutdown() {
return this.shutdown;
}
public Servlet getServlet() {
return this.servlet;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@ public class ReactiveWebServerFactoryCustomizer
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,6 +60,7 @@ public class ServletWebServerFactoryCustomizer
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,18 @@
package org.springframework.boot.autoconfigure.web.reactive;
import java.net.InetAddress;
import java.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.Ssl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -71,4 +75,14 @@ class ReactiveWebServerFactoryCustomizerTests {
verify(factory).setSsl(ssl);
}
@Test
void whenGracePeriodPropertyIsSetThenGracePeriodIsCustomized() {
this.properties.getShutdown().setGracePeriod(Duration.ofSeconds(30));
ConfigurableReactiveWebServerFactory factory = mock(ConfigurableReactiveWebServerFactory.class);
this.customizer.customize(factory);
ArgumentCaptor<Shutdown> shutdownCaptor = ArgumentCaptor.forClass(Shutdown.class);
verify(factory).setShutdown(shutdownCaptor.capture());
assertThat(shutdownCaptor.getValue().getGracePeriod()).isEqualTo(Duration.ofSeconds(30));
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.web.servlet;
import java.io.File;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@@ -29,6 +30,7 @@ import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.boot.web.servlet.server.Jsp;
@@ -154,6 +156,18 @@ class ServletWebServerFactoryCustomizerTests {
assertThat(sessionCaptor.getValue().getStoreDir()).isEqualTo(new File("myfolder"));
}
@Test
void whenGracePeriodPropertyIsSetThenGracePeriodIsCustomized() {
Map<String, String> map = new HashMap<>();
map.put("server.shutdown.grace-period", "30s");
bindProperties(map);
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
ArgumentCaptor<Shutdown> shutdownCaptor = ArgumentCaptor.forClass(Shutdown.class);
verify(factory).setShutdown(shutdownCaptor.capture());
assertThat(shutdownCaptor.getValue().getGracePeriod()).isEqualTo(Duration.ofSeconds(30));
}
private void bindProperties(Map<String, String> map) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(map);
new Binder(source).bind("server", Bindable.ofInstance(this.properties));