Spring Boot 项目优雅关闭常用两种方案,一种是使用kill -2 和 -15 发送程序终止信号,另一种是使用spring-boot-actuator包提供一个优雅下线接口,这两种方案根据自己喜好来,万不得已别用kill -9就行,个人推荐第一种,对于Spring Boot 项目来说两种方法只是入口不同,最终关闭逻辑都是一样的,具体的逻辑会在接下来阐述。
只要是Spring项目无论是何种方法最终都是调用到Spring的AbstractApplicationContext.doClose() 方法来实现优雅关闭,这里介绍的两种方法只是入口不同而已,在后面方案中会详细介绍。
PS:Spring Boot版本 ≥ 2.0
不同版本可能实现不同不过最终都是调用到AbstractApplicationContext.doClose() 方法来实现的关闭。
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是一个应用监控框架同时也提供了SpringBoot优雅下线的接口,原理还是一样的最终会调用到AbstractApplicationContext.doClose() 方法来实现的关闭,也可以自己写一个接口去调用AbstractApplicationContext.doClose(),这个有兴趣可以自己实现。
org.springframework.boot spring-boot-starter-actuator
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是Java规范JSR-250引入的注解,定义了对象的销毁工作,同一期规范中还有注解@Resource,Spring也支持了这些注解,会在Spring提供的一个后置处理器InitDestroyAnnotationBeanPostProcessor中调用的,当Spring容器销毁时会调用到@PreDestroy标记的方法,在这个方法中自行处理关闭逻辑即可。
内嵌Tomcat会使用到TomcatWebServer,TomcatWebServer提供了一个优雅关闭Tomcat的方法shutDownGracefully,如果不配置默认是直接关闭,需要在配置文件中配置server.shutdown = graceful 开启优雅关闭。