回答
Spring Boot 应用的优雅停机是为了应用在关闭时,能够合理地释放资源,完成一些必要的清理工作,并保证不影响正在处理的请求。
要实现 Spring Boot 的优雅停机,可以有如下几种方式:
- 启用 Spring Boot 的
Graceful Shutdown
功能
在 Spring Boot 2.3+ 中,它内置了优雅停机支持只需在 application.properties
启用即可,参数配置:server.shutdown=graceful
。
- 定义
@PreDestroy
或实现DisposableBean
接口
如果我们要执行一些清理逻辑,比如关闭数据库连接池,释放缓存等等,则可以使用 @PreDestroy
或者实现 DisposableBean
接口。
- 使用
ApplicationListener
监听上下文关闭事件
我们也可以使用 ApplicationListener 来监听 ContextClosedEvent 事件来实现我们的清理逻辑。
Spring Boot 优雅停机方式
1、启用 Spring Boot 优雅停机
Spring Boot 2.3+ 引入了对 graceful shutdown
的直接支持。我们只需要在配置文件中直接启用即可:
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=45s
server.shutdown=graceful
:就是在告诉容器,在关闭时不要立刻终止线程,而应该是等待所有正在处理的请求完成后再关闭。它的底层实现依赖于 Servlet 容器(如 Tomcat、Jetty)的Lifecycle
支持。spring.lifecycle.timeout-per-shutdown-phase=30s
:设置每个生命周期阶段的超时时间,默认是 30 秒。在这 30 秒内,如果线程执行完毕就会被强制终止。
2、@PreDestroy 和 DisposableBean
在 Spring 中,@PreDestroy
和 DisposableBean
都是用来管理 Bean 生命周期中销毁阶段的机制。
@PreDestroy
@PreDestroy
是 Java 中 JSR-250 规范的注解,它用于标记 Bean 销毁之前需要执行的方法。被它标记的的方法会在容器销毁该 Bean 时调用。
所以,利用它我们可以实现一些资源清理的工作。
@Component
public class DatabaseConnectionPool {
@PreDestroy
public void close() {
System.out.println("关闭数据库连接...");
}
}
DisposableBean 接口
实现 DisposableBean
接口的 destroy()
就可以了:
@Component
public class DatabaseConnectionPool implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("关闭数据库连接...");
}
}
对于 @PreDestroy
和 DisposableBean
,Spring 会在 AbstractApplicationContext
的 doClose()
中,会依次调用所有@PreDestroy
标记的方法和实现 DisposableBean
接口的类。
3、使用 ApplicationListener 监听上下文关闭事件
当应用程序关闭时,Spring 会触发 ContextClosedEvent
事件,所以我们只需要监听这个事件就可以自定义我们的关闭逻辑了。
@Component
public class GracefulShutdownListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
// 添加关闭资源的逻辑
}
}
SpringBoot应用的优雅停机如何触发
目前有两种主流方式来触发 Spring Boot 的优雅停机:
- kill -SIGTERM PID
- shutdown 端点
kill -SIGTERM PID