Graceful shutdown 应用
Graceful shutdown说明
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. 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. Jetty, Reactor Netty, and Tomcat will stop accepting requests at the network layer. Undertow will accept requests but respond immediately with a service unavailable (503) response.
版本要求
对Tomcat有版本要求,不能低于9.0.33
Graceful shutdown with Tomcat requires Tomcat 9.0.33 or later.
配置方式
# 配置启用graceful
server.shutdown=graceful
# 配置超时等待活跃请求的时间,默认30s
spring.lifecycle.timeout-per-shutdown-phase=30s
特别说明
监控的是SIGTERM信号,SIGTERM是Linux提供的一种优雅关闭服务的方式,通过发出一个软件终止的信号来给应用程序一个缓冲的时间,相对于SIGKILL这种,就会直接杀掉进程。
Using graceful shutdown with your IDE may not work properly if it does not send a proper SIGTERM
signal. See the documentation of your IDE for more details.
验证
只需简单配置后,即可使用,启动服务,发起请求。
@Slf4j
@RestController
public class TestController {
@RequestMapping("/activeRequest")
public void activeRequest() throws InterruptedException {
log.info("【请求执行中】");
/**
* spring.lifecycle.timeout-per-shutdown-phase=30s
* 配置为等待30秒,因此在执行改方法后的10秒内,如果停止了web服务,依然会等待改方法执行完毕
*/
Thread.sleep(10000);
log.info("【请求执行结束】");
}
}
输出日志中,可以看到,当发起停止服务时,打印了Commencing graceful shutdown. Waiting for active requests to complete
,表示开始等待活跃的请求执行结束。
当执行结束后,打印Graceful shutdown complete
,开始关闭其他相关资源。
2023-03-18 10:31:41.582 INFO 18056 --- [nio-8080-exec-1] c.w.controller.安全关闭WEB服务.TestController : 【请求执行中】
Disconnected from the target VM, address: '127.0.0.1:51695', transport: 'socket'
2023-03-18 10:31:45.158 INFO 18056 --- [extShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2023-03-18 10:31:51.593 INFO 18056 --- [nio-8080-exec-1] c.w.controller.安全关闭WEB服务.TestController : 【请求执行结束】耗时:10007毫秒
2023-03-18 10:31:51.656 INFO 18056 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
2023-03-18 10:31:51.664 INFO 18056 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2023-03-18 10:31:51.665 INFO 18056 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
2023-03-18 10:31:51.677 INFO 18056 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closed
如果把spring.lifecycle.timeout-per-shutdown-phase=10s
配置改为10秒,而activeRequest
方法要执行20秒
@RequestMapping("/activeRequest")
public void activeRequest() throws InterruptedException {
log.info("【请求执行中】");
Stopwatch started = Stopwatch.createStarted();
/**
* spring.lifecycle.timeout-per-shutdown-phase=10s
* 配置为等待10秒,因此改方法最终不会执行完成。
*/
Thread.sleep(20000);
log.info("【请求执行结束】耗时:" + started.elapsed(TimeUnit.MILLISECONDS) + "毫秒");
}
从日志输出Graceful shutdown aborted with one or more requests still active
可以看出,最终直接终止了一个或多个依然活动的请求,【请求执行结束】
也没有得到输出。
2023-03-18 10:57:16.020 INFO 22520 --- [nio-8080-exec-1] c.w.controller.安全关闭WEB服务.TestController : 【请求执行中】
Disconnected from the target VM, address: '127.0.0.1:52322', transport: 'socket'
2023-03-18 10:57:17.471 INFO 22520 --- [extShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2023-03-18 10:57:27.478 INFO 22520 --- [extShutdownHook] o.s.c.support.DefaultLifecycleProcessor : Failed to shut down 1 bean with phase value 2147483647 within timeout of 10000ms: [webServerGracefulShutdown]
2023-03-18 10:57:27.510 INFO 22520 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown aborted with one or more requests still active
2023-03-18 10:57:29.790 INFO 22520 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2023-03-18 10:57:29.791 INFO 22520 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
如果不配置server.shutdown=graceful
,则不会有任何提示以及等待活跃请求执行完成的行为,而是直接关闭服务。
2023-03-18 11:00:15.719 INFO 3376 --- [nio-8080-exec-1] c.w.controller.安全关闭WEB服务.TestController : 【请求执行中】
Disconnected from the target VM, address: '127.0.0.1:58704', transport: 'socket'
2023-03-18 11:00:21.086 INFO 3376 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2023-03-18 11:00:21.086 INFO 3376 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
2023-03-18 11:00:21.099 INFO 3376 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closed
换成Undertow服务也支持
2023-03-18 13:53:53.435 INFO 10804 --- [ XNIO-1 task-1] c.w.controller.安全关闭WEB服务.TestController : 【请求执行中】
Disconnected from the target VM, address: '127.0.0.1:63225', transport: 'socket'
2023-03-18 13:53:54.546 INFO 10804 --- [extShutdownHook] o.s.b.w.e.undertow.UndertowWebServer : Commencing graceful shutdown. Waiting for active requests to complete
2023-03-18 13:54:03.451 INFO 10804 --- [ XNIO-1 task-1] c.w.controller.安全关闭WEB服务.TestController : 【请求执行结束】耗时:10009毫秒
2023-03-18 13:54:03.478 INFO 10804 --- [ XNIO-1 task-1] o.s.b.w.e.undertow.UndertowWebServer : Graceful shutdown complete
2023-03-18 13:54:03.478 INFO 10804 --- [extShutdownHook] io.undertow : stopping server: Undertow - 2.2.3.Final
2023-03-18 13:54:03.487 INFO 10804 --- [extShutdownHook] io.undertow.servlet : Destroying Spring FrameworkServlet 'dispatcherServlet'
2023-03-18 13:54:03.489 INFO 10804 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2023-03-18 13:54:03.489 INFO 10804 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
2023-03-18 13:54:03.506 INFO 10804 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closed
与Tomcat不同之处,正如官方文档中说明那样,Undertow在关闭期间对于请求会直接返回503,而Tomcat则不会。
Undertow
Tomcat