mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Merge branch '3.1.x' into 3.2.x
Closes gh-40845
This commit is contained in:
commit
39ab959af8
@ -3,9 +3,15 @@
|
|||||||
Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications.
|
Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications.
|
||||||
It occurs as part of closing the application context and is performed in the earliest phase of stopping `SmartLifecycle` beans.
|
It occurs as part of closing the application context and is performed in the earliest phase of stopping `SmartLifecycle` beans.
|
||||||
This stop processing uses a timeout which provides a grace period during which existing requests will be allowed to complete but no new requests will be permitted.
|
This stop processing uses a timeout which provides a grace period during which existing requests will be allowed to complete but no new requests will be permitted.
|
||||||
|
|
||||||
The exact way in which new requests are not permitted varies depending on the web server that is being used.
|
The exact way in which new requests are not permitted varies depending on the web server that is being used.
|
||||||
Jetty, Reactor Netty, and Tomcat will stop accepting requests at the network layer.
|
Implementations may stop accepting requests at the network layer, or they may return a response with a specific HTTP status code or HTTP header.
|
||||||
Undertow will accept requests but respond immediately with a service unavailable (503) response.
|
The use of persistent connections can also change the way that requests stop being accepted.
|
||||||
|
|
||||||
|
TIP: To learn about more the specific method used with your web server, see the `shutDownGracefully` javadoc for {spring-boot-module-api}/web/embedded/tomcat/TomcatWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[TomcatWebServer], {spring-boot-module-api}/web/embedded/netty/NettyWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[NettyWebServer], {spring-boot-module-api}/web/embedded/jetty/JettyWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[JettyWebServer] or {spring-boot-module-api}/web/embedded/undertow/UndertowWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[UndertowWebServer].
|
||||||
|
|
||||||
|
Jetty, Reactor Netty, and Tomcat will stop accepting new requests at the network layer.
|
||||||
|
Undertow will accept new connections but respond immediately with a service unavailable (503) response.
|
||||||
|
|
||||||
NOTE: Graceful shutdown with Tomcat requires Tomcat 9.0.33 or later.
|
NOTE: Graceful shutdown with Tomcat requires Tomcat 9.0.33 or later.
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -293,6 +293,15 @@ public class JettyWebServer implements WebServer {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a graceful shutdown of the Jetty web server. Handling of new requests is
|
||||||
|
* prevented and the given {@code callback} is invoked at the end of the attempt. The
|
||||||
|
* attempt can be explicitly ended by invoking {@link #stop}.
|
||||||
|
* <p>
|
||||||
|
* Once shutdown has been initiated Jetty will reject any new connections. Requests on
|
||||||
|
* existing connections will be accepted, however, a {@code Connection: close} header
|
||||||
|
* will be returned in the response.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||||
if (this.gracefulShutdown == null) {
|
if (this.gracefulShutdown == null) {
|
||||||
|
@ -196,6 +196,14 @@ public class NettyWebServer implements WebServer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a graceful shutdown of the Netty web server. Handling of new requests is
|
||||||
|
* prevented and the given {@code callback} is invoked at the end of the attempt. The
|
||||||
|
* attempt can be explicitly ended by invoking {@link #stop}.
|
||||||
|
* <p>
|
||||||
|
* Once shutdown has been initiated Netty will reject any new connections. Requests +
|
||||||
|
* on existing idle connections will also be rejected.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||||
if (this.gracefulShutdown == null) {
|
if (this.gracefulShutdown == null) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -422,6 +422,14 @@ public class TomcatWebServer implements WebServer {
|
|||||||
return this.tomcat;
|
return this.tomcat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a graceful shutdown of the Tomcat web server. Handling of new requests is
|
||||||
|
* prevented and the given {@code callback} is invoked at the end of the attempt. The
|
||||||
|
* attempt can be explicitly ended by invoking {@link #stop}.
|
||||||
|
* <p>
|
||||||
|
* Once shutdown has been initiated Tomcat will reject any new connections. Requests
|
||||||
|
* on existing idle connections will also be rejected.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||||
if (this.gracefulShutdown == null) {
|
if (this.gracefulShutdown == null) {
|
||||||
|
@ -298,6 +298,14 @@ public class UndertowWebServer implements WebServer {
|
|||||||
return ports.get(0).getNumber();
|
return ports.get(0).getNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a graceful shutdown of the Undertow web server. Handling of new requests
|
||||||
|
* is prevented and the given {@code callback} is invoked at the end of the attempt.
|
||||||
|
* The attempt can be explicitly ended by invoking {@link #stop}.
|
||||||
|
* <p>
|
||||||
|
* Once shutdown has been initiated Undertow will return an {@code HTTP 503} response
|
||||||
|
* for any new or existing connections.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||||
if (this.gracefulShutdown == null) {
|
if (this.gracefulShutdown == null) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -36,6 +36,8 @@ import io.undertow.Undertow.Builder;
|
|||||||
import io.undertow.servlet.api.DeploymentInfo;
|
import io.undertow.servlet.api.DeploymentInfo;
|
||||||
import io.undertow.servlet.api.ServletContainer;
|
import io.undertow.servlet.api.ServletContainer;
|
||||||
import jakarta.servlet.ServletRegistration.Dynamic;
|
import jakarta.servlet.ServletRegistration.Dynamic;
|
||||||
|
import org.apache.hc.client5.http.classic.HttpClient;
|
||||||
|
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.jasper.servlet.JspServlet;
|
import org.apache.jasper.servlet.JspServlet;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
@ -212,18 +214,45 @@ class UndertowServletWebServerFactoryTests extends AbstractServletWebServerFacto
|
|||||||
this.webServer.stop();
|
this.webServer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenServerIsShuttingDownARequestOnAnIdleConnectionAreRejectedWithServiceUnavailable() throws Exception {
|
||||||
|
AbstractServletWebServerFactory factory = getFactory();
|
||||||
|
factory.setShutdown(Shutdown.GRACEFUL);
|
||||||
|
BlockingServlet blockingServlet = new BlockingServlet();
|
||||||
|
this.webServer = factory.getWebServer((context) -> {
|
||||||
|
Dynamic registration = context.addServlet("blockingServlet", blockingServlet);
|
||||||
|
registration.addMapping("/blocking");
|
||||||
|
registration.setAsyncSupported(true);
|
||||||
|
});
|
||||||
|
HttpClient httpClient = HttpClients.createMinimal();
|
||||||
|
this.webServer.start();
|
||||||
|
int port = this.webServer.getPort();
|
||||||
|
Future<Object> keepAliveRequest = initiateGetRequest(httpClient, port, "/blocking");
|
||||||
|
blockingServlet.awaitQueue();
|
||||||
|
blockingServlet.admitOne();
|
||||||
|
assertThat(keepAliveRequest.get()).isInstanceOf(HttpResponse.class);
|
||||||
|
Future<Object> request = initiateGetRequest(port, "/blocking");
|
||||||
|
blockingServlet.awaitQueue();
|
||||||
|
this.webServer.shutDownGracefully((result) -> {
|
||||||
|
});
|
||||||
|
HttpResponse idleConnectionResponse = (HttpResponse) initiateGetRequest(httpClient, port, "/").get();
|
||||||
|
assertThat(idleConnectionResponse.getCode()).isEqualTo(503);
|
||||||
|
blockingServlet.admitOne();
|
||||||
|
Object response = request.get();
|
||||||
|
assertThat(response).isInstanceOf(HttpResponse.class);
|
||||||
|
this.webServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
@Override
|
||||||
@Disabled("Restart after stop is not supported with Undertow")
|
@Disabled("Restart after stop is not supported with Undertow")
|
||||||
protected void restartAfterStop() {
|
protected void restartAfterStop() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
@Override
|
||||||
@Disabled("Undertow's architecture prevents separating stop and destroy")
|
@Disabled("Undertow's architecture prevents separating stop and destroy")
|
||||||
protected void servletContextListenerContextDestroyedIsNotCalledWhenContainerIsStopped() {
|
protected void servletContextListenerContextDestroyedIsNotCalledWhenContainerIsStopped() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testAccessLog(String prefix, String suffix, String expectedFile)
|
private void testAccessLog(String prefix, String suffix, String expectedFile)
|
||||||
|
Loading…
Reference in New Issue
Block a user