前言
Spring Boot 项目优雅关闭常用两种方案,一种是使用kill -2 和 -15 发送程序终止信号,另一种是使用spring-boot-actuator包提供一个优雅下线接口,这两种方案根据自己喜好来,万不得已别用kill -9就行,个人推荐第一种,对于Spring Boot 项目来说两种方法只是入口不同,最终关闭逻辑都是一样的,具体的逻辑会在接下来阐述。
一、Spring Boot 项目优雅关闭核心逻辑
只要是Spring项目无论是何种方法最终都是调用到Spring的AbstractApplicationContext.doClose() 方法来实现优雅关闭,这里介绍的两种方法只是入口不同而已,在后面方案中会详细介绍。
PS:Spring Boot版本 ≥ 2.0
不同版本可能实现不同不过最终都是调用到AbstractApplicationContext.doClose() 方法来实现的关闭。
二、方案原理和实现(这里不包含Tomcat优雅关闭在下面扩展中会有详细介绍)
方案一原理和实现示例(通过kill -2 和 -15 优雅关闭,这种方式SpringBoot是默认支持的)
kill -2 和 -15 是操作系统给该进程发送一个信号通知,告知应用主动关闭,应用可以监听并接收到信号,可以完成一些关闭回收等动作,然后自我停止,这些关闭信号并不是由spring感知,而是由java线程Signal Dispatcher监听,此线程将收到的信号交给JVM,JVM判断信号种类,如果是-2/-15等关闭类型,则交由java.lang.Shutdown完成关闭,关闭前会触发所有的shutdown hook,Runtime.getRuntime().addShutdownHook() 可以添加自定义的 shutdown hook。
在启动类SpringApplication中会调用自己的refreshContext方法,通过 SpringApplicationShutdownHook.addRuntimeShutdownHook() 方法调用 Runtime.getRuntime().addShutdownHook() 添加自定义的 shutdown hook,在进程接收到kill -2 或 -15 时触发。
SpringApplicationShutdownHook实现了 Runnable 重写了run 方法,当进程接收到kill -2 或 -15 时触发执行关闭逻辑。
演示
# 发送一个关闭信号通知
kill -15 pid
这里可以看到数据源定时任务等工具的关闭日志,还会看到一个线程池的关闭日志,这个后面扩展中会详细说明。
方案二原理和实现示例(通过spring-boot-actuator提供优雅关闭接口)
spring-boot-actuator是一个应用监控框架同时也提供了SpringBoot优雅下线的接口,原理还是一样的最终会调用到AbstractApplicationContext.doClose() 方法来实现的关闭,也可以自己写一个接口去调用AbstractApplicationContext.doClose(),这个有兴趣可以自己实现。
代码实现
- POM
<!-- 监控 版本同步spring-boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置文件
spring:
lifecycle:
# 优雅下线超时时间
timeout-per-shutdown-phase: 1m
# 暴露 shutdown 接口
# 调用 curl -X POST http://127.0.0.1:18080/actuator/shutdown
management:
server:
### 端口
port: 18080
### 允许服务地址
address: 127.0.0.1
endpoint:
### 是否开启下线 默认关闭
shutdown:
enabled: true
endpoints:
web:
exposure:
### 暴露接口
include: shutdown
演示
这里看到的效果和方案一是一样的,可以看到数据源定时任务等工具的关闭日志,只是执行线程变了,线程池关闭看扩展。
三、扩展
通过 @PreDestroy 注解关闭自定义线程池和其它自定义工具
@PreDestroy是Java规范JSR-250引入的注解,定义了对象的销毁工作,同一期规范中还有注解@Resource,Spring也支持了这些注解,会在Spring提供的一个后置处理器InitDestroyAnnotationBeanPostProcessor中调用的,当Spring容器销毁时会调用到@PreDestroy标记的方法,在这个方法中自行处理关闭逻辑即可。
优雅关闭内嵌Tomcat
内嵌Tomcat会使用到TomcatWebServer,TomcatWebServer提供了一个优雅关闭Tomcat的方法shutDownGracefully,如果不配置默认是直接关闭,需要在配置文件中配置server.shutdown = graceful 开启优雅关闭。