Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码)
作者:mmseoamin日期:2023-12-25

目录

  • 前言
  • 一、Spring Boot 项目优雅关闭核心逻辑
  • 二、方案原理和实现(这里不包含Tomcat优雅关闭在下面扩展中会有详细介绍)
    • 方案一原理和实现示例(通过kill -2 和 -15 优雅关闭,这种方式SpringBoot是默认支持的)
      • 演示
      • 方案二原理和实现示例(通过spring-boot-actuator提供优雅关闭接口)
        • 代码实现
        • 演示
        • 三、扩展
          • 通过 @PreDestroy 注解关闭自定义线程池和其它自定义工具
          • 优雅关闭内嵌Tomcat

            前言

            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 hookRuntime.getRuntime().addShutdownHook() 可以添加自定义的 shutdown hook

            在启动类SpringApplication中会调用自己的refreshContext方法,通过 SpringApplicationShutdownHook.addRuntimeShutdownHook() 方法调用 Runtime.getRuntime().addShutdownHook() 添加自定义的 shutdown hook,在进程接收到kill -2 或 -15 时触发。

            Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第1张

            SpringApplicationShutdownHook实现了 Runnable 重写了run 方法,当进程接收到kill -2 或 -15 时触发执行关闭逻辑。

            Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第2张

            演示

            # 发送一个关闭信号通知
            kill -15 pid
            

            Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第3张

            这里可以看到数据源定时任务等工具的关闭日志,还会看到一个线程池的关闭日志,这个后面扩展中会详细说明。

            方案二原理和实现示例(通过spring-boot-actuator提供优雅关闭接口)

            spring-boot-actuator是一个应用监控框架同时也提供了SpringBoot优雅下线的接口,原理还是一样的最终会调用到AbstractApplicationContext.doClose() 方法来实现的关闭,也可以自己写一个接口去调用AbstractApplicationContext.doClose(),这个有兴趣可以自己实现。

            代码实现

            • POM
                      
                      
                          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
                

                演示

                Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第4张

                这里看到的效果和方案一是一样的,可以看到数据源定时任务等工具的关闭日志,只是执行线程变了,线程池关闭看扩展。

                三、扩展

                通过 @PreDestroy 注解关闭自定义线程池和其它自定义工具

                @PreDestroy是Java规范JSR-250引入的注解,定义了对象的销毁工作,同一期规范中还有注解@Resource,Spring也支持了这些注解,会在Spring提供的一个后置处理器InitDestroyAnnotationBeanPostProcessor中调用的,当Spring容器销毁时会调用到@PreDestroy标记的方法,在这个方法中自行处理关闭逻辑即可。

                Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第5张

                优雅关闭内嵌Tomcat

                内嵌Tomcat会使用到TomcatWebServerTomcatWebServer提供了一个优雅关闭Tomcat的方法shutDownGracefully,如果不配置默认是直接关闭,需要在配置文件中配置server.shutdown = graceful 开启优雅关闭。

                Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第6张

                Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第7张

                Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码),在这里插入图片描述,第8张