news 2026/5/12 12:09:02

springboot优雅关机方案分享:逻辑实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
springboot优雅关机方案分享:逻辑实现

前言

前两次分享,我们已经介绍过了k8s节点关机的流程和优雅关机要实现的流程,今天我们来一起来看下具体的代码实现,主要内容如下:

  • SIGTERM监听逻辑
  • 预关机逻辑
  • 各个组件的关机逻辑和监控逻辑

实现过程

前置要点

前面我们说了,本项目实际是个spring-boot-starter,所以你要先创建spring-boot-starter的项目,这里就不赘述具体过程了,具体可以参考之前的分享: [[编写你的第一个spring-boot-starter]]

这里提几个需要注意的点:

pom依赖

这块有两个标签要注意,scopeoptional,组合使用,可以确保依赖的灵活性,同时避免依赖冲突:

scope(作用域)

依赖的作用范围,控制依赖的使用范围,这里的provided表示编译和测试时有效,运行时由运行环境提供,也就是不会打进包里。这样可以确保我们的starter引入项目后不会影响原项目的pom依赖关系,带来未知风险。常见的作用域还有:

  • compile(默认):编译、测试、运行都有效,会打包
  • provided:编译和测试时有效,运行时由容器提供
  • runtime:运行时需要,编译时不需要
  • test:仅测试时使用
  • system:类似provided,但需要显式指定本地jar路径

optional(可选依赖)

当设置为true时:

  • 不会传递依赖:即使其他项目依赖本项目,也不会继承这个可选依赖
  • 需要显式声明:使用者必须主动声明才能使用
  • 场景:可选功能、有冲突的依赖、特定环境才需要的依赖

当然,这两个pom标签通常是配合spring-boot的条件配置来使用,或者你可以确保代码运行时一定包含对应的依赖,否则会导致运行时异常。

spring.factories

这个文件是starter的核心,当我们的项目被引入时,springboot会根据我们的spring.factories初始化我们的starter,并根据我们的配置类,完成加载和配置。这个项目通常位于src/main/resources/META-INF/spring.factories下,配置内容如下:

# src/main/resources/META-INF/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ io.syske.springboot.starter.config.GracefulShutdownAutoConfiguration,\ io.syske.springboot.starter.compent.SpringContextUtilConfig

配置的内容就是我们的配置类,有多个配置类用逗号分隔开就行。因为配置类本身是支持import的,所以我们通常只需要配置一个主配置类即可,其他的直接@Import即可:

我这里配置两个是由于另一个类不能条件注入,所以单独配置了,具体大家根据自己实际情况看。

条件配置

这里简单说下本次用到的几个条件注解:

  • @ConditionalOnProperty:这个注解的作用是根据某个配置文件判断,类或者方法支付要执行,通常和@Configuration或者@Bean组合使用。在我们上面的截图中,实际就是通过判断优雅关机开关是否开启进行条件配置,如果条件缺失,我们给了默认值true
  • @ConditionalOnMissingBean:当缺少某个类的bean时配置,类似单例模式
  • @ConditionalOnClass:当有指定的class的时候触发配置。这个就是我前面说的要和pom那两个标签组合的条件配置。所有的关机组件逻辑都要加上这个判断,确保核心类存在(也就是依赖存在)时,才配置对应组件的关机逻辑。
  • @ConditionalOnWebApplication:这个注解是加在Tomcat的关机组件上的,确保它是WebApplication

SIGTERM逻辑

核心实现其实就是创建一个bean,并在bean初始化逻辑中定义一个处理TERM信号的Signal,并在Signalhandle逻辑中加入我们的预关机逻辑即可:

try { Signal signal = new Signal("TERM"); Signal.handle(signal, sig -> { logger.info("Received SIGTERM signal from K8s, starting graceful shutdown"); // 处理关机信号 handleShutdownSignal(); }); logger.info("SIGTERM handler registered successfully"); } catch (Exception e) { logger.warn("Failed to register SIGTERM handler: {}, using shutdown hook as fallback", e.getMessage()); setupShutdownHook(); }

这里我就直接放截图了,处理关机信号逻辑:

然后就是预关机逻辑:

这里简单说下:

  • 服务标记为不健康:这里我自定义了一个healthIndicator,实际没太大作用,因为我们的服务健康检查并不依赖这个,如果你们的服务健康检查依赖的是springboot的健康检查机制,那实现自定义健康检查则是需要考虑的。
  • 等待负载均衡感知:这个实际和网关有关系,在本项目中非必须,逻辑中只是睡了5秒。
  • 执行优雅关机:这里就是根据我们自定义的优先级,依次关机各个组件。

下面,我们逐步介绍各组件的实现逻辑。

Tomcat

监控逻辑

tomcat优雅关机要实现TomcatConnectorCustomizer接口,并实现customize方法,主要是为了关机时能获取到连接器,同时设置Executor实现统计活跃请求数的逻辑:

计数执行器逻辑:

组件核心逻辑

核心逻辑就三步:

// 1. 暂停接收新请求 pauseConnector(); // 2. 等待活跃请求完成 waitForActiveRequests(); // 3. 关闭连接器 stopConnector();

停止接受新请求实际就是调用连接器的暂停方法:

等待活跃请求完成,实际就是等待线程池关闭:

关闭线程池实际就是调用shutdown方法,超时强制关闭实际就是超时后调用shutdownNow,然后再等待:

线程池考虑了org.apache.tomcat.util.threads.ThreadPoolExecutorjava.util.concurrent.ThreadPoolExecutor,是分别处理,实际两者是继承关系,其实可以省略第一个:

停止连接器也很简单,就是调用stop方法,不过关闭前需要判断下,避免重复关闭(springboot本身的关机逻辑也会触发关闭):

RPC提供者

监控逻辑

监控这里的逻辑是通过sofa-rpcFilter来实现的:

  • 统计请求数量
  • 服务注册:这里的注册实际是个辅助逻辑,确保没有正常注册的提供者在调用过程中注册。真正的服务提供中注册逻辑是基于ApplicationRunner实现的

关于拦截器的注册有几个点:

  • 拦截器只能通过SPI方式注入,且要正确配置@AutoActive@Extension,需要注意的是@Extension配置的名称要与com.alipay.sofa.rpc.filter.filter文件中配置的名称一致,否则不生效

  • com.alipay.sofa.rpc.filter.filter文件必须正确配置:配置的key@Extension配置的值,value是拦截器的全类名:

注意:这里还有个比较坑的点,sofa-boot的拦截器不能直接使用springbootbean,所以需要借助SpringContextUtil,这也是我的spring.factories里面还配置了SpringContextUtilConfig的原因。因为拦截器没法实现条件配置,所以SpringContextUtil也不能条件配置。

目标发现逻辑

Runer核心逻辑如下:

这里搞了好久一直没有搞定,最后是在sofa-boot的代码中找到的,然后直接解决了提供者发现问题:

组件核心逻辑

组件核心逻辑就三步:

  1. 标记为关闭状态:实际这一步并没有拦截请求
  2. 取消服务者注册:这一步直接调用unExport方法

  1. 等待处理中的请求完成:判断活跃请求的逻辑来自监控逻辑

ActiveMQ

监控逻辑

activeMQ的监控逻辑实际是通过DefaultMessageListenerContainer直接获取的,所以监控逻辑本身没什么特殊的:

目标发现逻辑

activeMQ的服务发现逻辑也比较简单,核心其实就是将JmsListenerEndpointRegistry注入我们的关机组件,关机组件要通过这个对象获取所有的DefaultMessageListenerContainer消费者容器:

组件核心逻辑

核心逻辑如下:

  1. 发现所有监听者,并打印容器状态
  2. 停止容器:调用stop方法

  1. 等待处理中的消息处理完成:

RocketMQ

监控逻辑

监控逻辑是根据容器是否是running状态判断的,比较简单:

目标发现逻辑

组件的发现逻辑是通过spring的后置处理器来完成的,将所有的DefaultRocketMQListenerContainer收集起来。

组件核心逻辑

核心逻辑如下:

  1. 停止监听器逻辑:这里是调用stop方法进行停止

  1. 等待处理中的容器停止:这里直接用的是容器状态判断的,因为消费中的消息数量,需要调用rpcketMQ的服务端接口,实际意义不大,而且要依赖外部服务,所以直接判断容器状态。


线程池

监控逻辑

没什么监控逻辑,实际就是直接打印线程池的活跃的线程数:

目标发现逻辑

发现逻辑也是基于spring的后置处理器完成的,根据不通的线程池注册到不通的集合中:

组件核心逻辑

核心逻辑:不同的线程池挨个关闭

关闭逻辑都差不多:

  1. 线程池关机:调用shutdown方法
  2. 等待完成:调用awaitTermination,超时后调用shutdownNow

ForkJoinPool比较特殊,只能等待:

RPC客户端

客户端也叫运行时环境,这个销毁就比较简单了,没有监控,直接通过反射调用RpcRuntimeContextdestroy方法即可。

至此,我们的k8s环境下的spring-boot服务优雅关机方案就完成了。下面我们简单总结下:

总结

我们用了三篇文章,和大家聊清楚了 K8s 环境下 Spring Boot 服务“优雅关机”这回事儿:

第一篇:先弄懂 K8s 是怎么关机的
带大家梳理了 K8s 节点和 Pod 关闭的基本流程。了解了它的“套路”,咱们自己设计方案时才能心里有底。

第二篇:咱们的方案长啥样?
知道了 K8s 的流程,那我们的 Spring Boot 服务该怎么配合着“优雅退场”呢?这一篇就讲了咱们的整体设计思路,关机要分几步、每一步要注意啥,都给大家掰扯明白了。

第三篇:动手!把代码写出来
光说不练假把式。最后一篇咱们直接上代码,手把手讲解了怎么监控组件、怎么发现需要关闭的目标,再把整个关闭流程像拼积木一样组合起来。让大家能从代码层面真正搞懂怎么写。

当然了,咱们现在这个方案还不是“终极完美版”。除了之前提到的(比如TomcatRPC提供者可以同时关)这些优化点,其实还有一些地方可以做得更好。

比如说“重复关闭”的问题:虽然代码里加了判断,不会因为重复调用而报错,但Spring Boot底层的关机钩子 (ShutdownHook) 逻辑其实还在,这里未来还有更优雅的解法。

我们特意把这个点提出来,其实就是想“抛砖引玉”。优雅关机这件事,细节很多,也欢迎大家一起来思考、讨论,看看还有哪些地方可以优化得更好。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 9:30:49

Windows下如何运行M2FP?Docker一键启动,告别环境配置

Windows下如何运行M2FP?Docker一键启动,告别环境配置 🧩 M2FP 多人人体解析服务 (WebUI API) 从零部署的噩梦到一键启动的现实 在计算机视觉领域,人体解析(Human Parsing) 是一项关键的细粒度语义分割任…

作者头像 李华
网站建设 2026/5/10 1:40:28

Z-Image-Turbo GPU显存占用测试:1024×1024需要多少VRAM?

Z-Image-Turbo GPU显存占用测试:10241024需要多少VRAM? 阿里通义Z-Image-Turbo WebUI图像快速生成模型 二次开发构建by科哥 核心结论前置:在使用阿里通义Z-Image-Turbo进行10241024分辨率图像生成时,最低需约6.8GB VRAM&#xff…

作者头像 李华
网站建设 2026/5/7 11:04:44

python基于微信小程序的学生选课系统django_jk7zrvx5

文章目录项目概述技术架构核心功能特色与优势应用场景主要技术与实现手段系统设计与实现的思路系统设计方法java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!项目概述 Python基于微信小程序的学生选课系统&…

作者头像 李华
网站建设 2026/5/9 23:34:39

CSS属性继承性分类总结

CSS属性继承性分类总结:文本相关属性(如color、font-family)、列表样式和部分显示属性通常会被子元素继承;而盒模型(width、padding等)、定位、背景等布局属性不会继承。特殊情况下可用inherit强制继承&…

作者头像 李华
网站建设 2026/5/1 15:17:42

Z-Image-Turbo超现实主义艺术创作适配性

Z-Image-Turbo超现实主义艺术创作适配性 引言:AI图像生成的边界拓展与艺术表达新范式 随着生成式AI技术的迅猛发展,图像生成模型已从“能画”迈向“会意”的阶段。阿里通义推出的 Z-Image-Turbo WebUI 图像快速生成系统,凭借其高效的推理速…

作者头像 李华
网站建设 2026/5/3 2:50:01

从JDBC到MyBatis:开发效率提升300%的秘诀

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 请创建一个对比演示项目,分别用原生JDBC和MyBatis实现相同的用户管理功能(CRUD分页查询)。要求:1) 统计两种实现方式的代码行数差异…

作者头像 李华