还能这样玩?SpringBoot这些优雅停机的正确姿势你都会了吗?

springboot遇到dubbo优雅停机 JVM优雅停机机制 关于优雅停机,JVM提供了一个叫做ShutdownHook的东东,什么意思呢?就是说我们的应用程序只要在程序中向JVM注册了这个东东,JVM就会在收到关机信号(非kill -9),JVM就会在真正调用关机之前执行你注册在ShutdownHook里的东东,以达到程序在关闭前释放掉各种资源的目的。 注册方式: 注册ShutdownHook Runtime.getRuntime... 阅读详情

一、介绍

什么叫优雅停机?

简单的说,就是向应用进程发出停止指令之后,能保证正在执行的业务操作不受影响,直到操作运行完毕之后再停止服务。应用程序接收到停止指令之后,会进行如下操作:

  • 1.停止接收新的访问请求

  • 2.正在处理的请求,等待请求处理完毕;对于内部正在执行的其他任务,比如定时任务、mq 消费等等,也要等当前正在执行的任务执行完毕,并且不再启动新的任务

  • 3.当应用准备关闭的时候,按需向外发出信号,告知其他应用服务准备接手,以保证服务高可用

如果暴力的关闭应用程序,比如通过kill -9 <pid>命令强制直接关闭应用程序进程,可能会导致正在执行的任务数据丢失或者错乱,也可能会导致任务所持有的全局资源等不到释放,比如当前任务持有 redis 的锁,并且没有设置过期时间,当任务突然被终止并且没有主动释放锁,会导致其他进程因无法获取锁而不能处理业务。

那么如何在不影响正在执行的业务的情况下,将应用程序安全的进行关闭呢?

编辑切换为居中

添加图片注释,不超过 140 字(可选)

二、方案实践

SpringBoot 官方文档上,已经告诉开发者只需要实现特定接口即可监听到项目启动成功与关闭时的事件,相关接口如下:

  • CommandLineRunner接口:当应用启动成功后但在开始接受流量之前,会回调此接口的实现类,也可以实现ApplicationRunner接口,工作的方式与CommandLineRunner与之类似

  • DisposableBean接口:当应用正要被销毁前,会回调此接口的实现类,也可以使用@PreDestroy注解,被标记的方法也会被调用

基于此流程,我们可以创建一个服务监听类,用于监听到项目启动成功与关闭时的回调服务,示例代码如下:



@Component public class AppListener implements CommandLineRunner, DisposableBean { @Override public void run(String... args) throws Exception { System.out.println("应用启动成功,预加载相关数据"); } @Override public void destroy() throws Exception { System.out.println("应用正在关闭,清理相关数据"); } }

每一个SpringApplication在启用的时候,都会向 JVM 注册一个关闭钩子shutdown hook,以确保ApplicationContext在退出的时候,通过这个勾子通知 JVM,实现服务正常的关闭,以下介绍的所有关闭服务的方法,都是基于这一原理进行实现的。

2.1、方法一:通过Actuator的Endpoint机制关闭服务

使用此方法,需要先添加spring-boot-starter-actuator监控服务依赖包,



<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

默认配置下,shutdown端点是关闭的,需要在application.properties里配置里面开启:



management.endpoint.shutdown.enabled=true

虽然Actuator的端点,支持通过JMX或HTTP进行远程访问。而shutdown默认配置下是不支持HTTP进行Web访问的,所以使用HTTP请求进行关闭时的配置,也需要开启:



management.endpoints.web.exposure.include=shutdown

最后将SpringBoot服务启动之后,使用POST请求类型,调用以下接口,即可实现关闭服务!



http://127.0.0.1:8080/actuator/shutdown

编辑切换为居中

添加图片注释,不超过 140 字(可选)

2.2、方法二:使用ApplicationContext的close方法关闭服务

如果你不想添加spring-boot-starter-actuator监控服务依赖包来关停服务,也可以使用ApplicationContext的close方法来关停服务,他会自动销毁bean对象并关停服务。

只需要在应用启用的时候,获取ApplicationContext对象,然后在相关的位置调用close方法,就可以关闭服务。

示例代码如下:



@SpringBootApplication public class Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //启动10秒以后,自动关闭 context.close(); } }

当然我们也可以自己写一个Controller,获取对应的ApplicationContext对象,通过api操作调用close方法关停服务,示例代码如下:



@RestController public class ShutdownController implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } /** * 关闭服务 */ @GetMapping("/shutdown") public void shutdownContext() { ((ConfigurableApplicationContext) context).close(); } }

2.3、方法三:监听服务pid,通过kill方式关闭服务

通过api方式来关停服务,在很多人看来并不安全,因为一旦接口泄漏了,意味着用户可以随便请求这个接口来关闭服务,其影响不言而喻,因此很多人建议在服务端,通过其他的方式来关闭服务,比如通过进程命令方式来关停。

在springboot启动的时候将应用进程 ID 写入一个app.pid文件,生成的路径可以指定,然后通过脚本命令方式来关闭服务。

启动示例代码如下:



@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication application = new SpringApplication(Application.class); application.addListeners(new ApplicationPidFileWriter("/home/app/project1/app.pid")); application.run(); } }

通过如下命令方式,可以安全的关闭服务。



cat /home/app/project1/app.pid | xargs kill

这种方式,也是目前在linux操作系统中,使用较为普遍的一种解决方案,区别在于实现的方式可能不同,有的不用写文件,通过其他方式来获取应用进程 ID。

如果使用kill -9 <pid>的方式关闭服务,服务的监听器不会收到任何消息,类似于直接强杀应用进程,此方法不可取!

2.4、方法四:使用SpringApplication的exit方法关闭服务

通过调用一个SpringApplication.exit()方法也可以安全的退出程序,同时会返回一个退出码,这个退出码可以传递给所有的context,最后通过调用System.exit()可以将这个错误码也传给JVM。

示例代码如下:



@SpringBootApplication public class Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } //5秒后,关闭服务 exitApplication(context); } public static void exitApplication(ConfigurableApplicationContext context) { //获取退出码 int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0); //退出码传递给jvm,安全退出程序 System.exit(exitCode); } }

三、其他监听介绍

3.1、ApplicationListener

如果有些服务,比如定时任务,我们想在SpringBoot关闭数据源连接池之前,将其关闭,可以通过实现ApplicationListener接口,监听bean对象的变化情况,在bean对象销毁之前,执行相关的关闭任务。

示例代码如下:



@Component public class JobTaskListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { // 在spring bean容器销毁之前执行的事件,防止数据库连接池在任务终止前销毁 if (applicationEvent instanceof ContextClosedEvent) { System.out.println("关闭相关的定时任务"); } } }

3.2、PreDestroy

上文中,我们提到了实现DisposableBean接口,可以监听应用关闭前的回调处理,其实在自定义的方法上加@PreDestroy注解,也可以实现相同的效果。

示例代码如下:



@Component public class AppDestroyConfig { @PreDestroy public void PreDestroy(){ System.out.println("应用程序正在关闭。。。"); } }

四、小结

本位主要围绕如何安全的关闭SpringBoot服务,进行了一些方案操作的介绍,如果有疏漏的地方,欢迎网友批评指出!

                                              资源获取:
大家点赞、收藏、关注、评论啦 、查看👇🏻👇🏻👇🏻微信公众号获取联系方式👇🏻👇🏻👇🏻
精彩专栏推荐订阅:下方专栏👇🏻👇🏻👇🏻👇🏻

SpringBoot + Nacos + K8s 优雅停机 1 概念2 用案例说话案例前:k8s 停机流程k8s + springboot + nacos 案例案例优化3 再次优化mq 和 定任务流量控制4 小结。 阅读详情

相关推荐

Springboot项目平滑关闭及自动化关闭脚本

主要为大家详细介绍了Springboot项目平滑关闭及自动化关闭脚本,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Spring——项目优雅停机

当我们停止正在运行的应用程序或进程,底层操作系统会向进程发送终止信号。在没有启用任何优雅关闭机制的情况下(如:kill -9),Spring Boot 应用程序将在收到信号后立即终止。此一些没有执行完的程序就会直接退出,可能导致业务逻辑执行失败,在一些业务场景下:会出现数据不一致的情况,事务逻辑不会回滚。

CHIKA2333的博客 3314

Spring环境中正确关闭线程池的姿势

前言 在Java System#exit 无法退出程序的问题探索一文末尾提到优雅停机的一种实现方案,要借助Shutdown Hook进行实现,本文,将继续探索优雅停机中遇到的一些问题:应用中线程池的优雅关闭 线程池正确关闭姿势 在这一节,先不讨论应用中线程池该如何优雅关闭以达到优雅停机的效果,只是简单介绍一下线程池正确关闭姿势 为简化讨论的复杂性,本文的线程池均是指JDK中的java.util.concurrent.ThreadPoolExecutor 正确关闭线程池的关键是 shutdown +

七夜 1万+

SpringBoot关闭都做了哪些事?

本文我们开始分析SpringBoot关闭候都做了哪些事。核心流程梳理如下: * 从LiveBeansView移除掉维护的上下文 * 广播ContextClosedEvent事件 * 触发生命周期处理器的onClose方法,这里会stopBean,也就是触发那些Lifecycle实例的stop方法 * 销毁BeanFactory中的所有DisposableBean并清空一些缓存 * 关闭BeanFactory * 停止服务,这里会stop Tomcat * 将earlyApplicationListene

小小默:进无止境 2万+

Spring Boot 项目优雅关闭(两种完善解决方案附带示例代码)

Spring Boot 项目优雅关闭常用两种方案,一种是使用kill -2 和 -15 发送程序终止信号,另一种是使用spring-boot-actuator包提供一个优雅下线接口

kerwin的博客 1万+

java对接国外项目需要知识_高级Java程序员都必须要知道的JMX知识点,SpringBoot中也有使用的知识点。...

JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。前面是对JMX的介绍,那么JMX在我们日常的开发过程中,有什么实际的意义呢? 相信很多做Java开发的同学都使用过JDK自带的 jconsole 或者 jv...

weixin_33729566的博客 368

重学SpringBoot3-怎样优雅停机

在现代微服务架构中,优雅停机(Graceful Shutdown)是一项重要功能,可以确保服务在关闭处理完所有当前请求,避免突然终止连接或丢失数据。Spring Boot 3 提供了对优雅停机的内置支持,允许在关闭应用程序上下文期间为现有请求设置一个宽限期,同防止新请求进入。本文将详细介绍 Spring Boot 3 的优雅停机机制,重点分析 Tomcat 和 Reactor Netty 两种常用的嵌入式 Web 服务器的优雅停机流程。

CoderJia的学习之路 5588

SpringBootSpringBoot优雅停机机制

拒绝新请求:关闭过程开始后,系统不再接收新的用户请求。完成当前请求:对已接收的请求完成处理,避免突然中断。资源清理:在停机前,释放各种资源(数据库连接、线程池等),保证系统的状态完整性。优雅停机的核心是提供一种“无感知”的下线体验,让用户和系统都能安全退出。如果需要在停机执行特定的清理操作,比如关闭数据库连接或停止线程池,可以通过添加或实现接口。@Component@OverrideSystem.out.println("关闭数据库连接...");

学不会就接着学 2849

SpringBoot优雅停机正确姿势

文章来源:https://c1n.cn/Xu93t目录何为优雅关机kill 指令Runtime.addShutdownHookSpring 3.2.12SpringBoot再谈为了提醒明知故犯(在一坑里迭倒两次不是不多见),由于业务系统中大量使用了 SpringBoot embedded tomcat 的模式运行,在一些运维脚本中经常看到 Linux 中kill指令,...

石杉的架构笔记 4046

《学会 SpringBoot · 优雅停机方案》

Spring Boot 中,“优雅停机”(Graceful Shutdown)指的是在应用程序关闭,能够优雅地处理正在进行的请求和任务,而不是强制立即终止。这种机制可以确保在应用程序关闭,现有的请求能够被处理完毕,避免数据丢失或请求失败。优雅停机的特点处理现有请求:在接收到关闭信号后,应用会停止接收新的请求,但会继续处理已经接收到的请求。可配置的超:可以配置一个超间,在这个间内,应用会尝试完成所有正在进行的请求。如果超后仍有请求未完成,应用会强制关闭

山人行 1890

Spring Boot 优雅停机正确姿势

什么叫优雅停机?简单的说,就是向应用进程发出停止指令之后,能保证正在执行的业务操作不受影响,直到操作运行完毕之后再停止服务。应用程序接收到停止指令之后,会进行如下操作:1.停止接收新的访问请求2.正在处理的请求,等待请求处理完毕;对于内部正在执行的其他任务,比如定任务、mq 消费等等,也要等当前正在执行的任务执行完毕,并且不再启动新的任务3.当应用准备关闭候,按需向外发出信号,告知其他应用服务准备接手,以保证服务高可用如果暴力的关闭应用程序,比如通过。

tangjieqing的博客 1137

面试官:SpringBoot如何优雅停机

本文内容已收录至我的面试网站:www.javacn.site优雅停机(Graceful Shutdown)是指在服务器需要关闭或重启,能够先处理完当前正在进行的请求,然后再停止服务的操作。优雅停机的实现步骤主要分为以下几步:停止接收新的请求:首先,系统会停止接受新的请求,这样就不会有新的任务被添加到任务队列中。处理当前请求:系统会继续处理当前已经在处理中的请求,确保这些请求能够正常完成。这通常涉...

磊哥聊Java 1991

SpringBoot 优雅停机

目录linux常用的 kill 指令springboot 优雅停机/actuator/shutdown 端点不停机更新方案   linux常用的 kill 指令 kill -15:kill指令默认就是-15,只是发送一个 SIGTERM 信号通知进程终止,由进程自行决定怎么做,即进程不一定会终止。 kill -9:强制终止进程,进程会被立刻终止 kill -2:类似 Ctrl+C 退出,会先保存相关数据再终止进程。 一般不直接使用 kill -15,因为不一定能终止进程。 kill -9 过于

chy-x 的博客 4842

SpringBoot中3种优雅停机的实现方式

停止接收新的请求等待正在处理的请求完成关闭各种资源连接(数据库连接池、线程池、消息队列连接等)完成必要的清理工作最后退出应用提高用户体验,避免请求突然中断保障数据一致性,防止数据丢失JVM提供了ShutdownHook机制,允许在JVM关闭前执行自定义逻辑。通过注册自定义的ShutdownHook,我们可以实现更加精细和灵活的优雅停机控制。这种方式的优势在于可以精确控制资源释放顺序,适合有复杂资源管理需求的应用

风象南的专栏 4517
上一篇: 好家伙!阿里最新SpringBoot进阶笔记涵盖了SpringBoot所有骚操作
下一篇: 阿里工作10年,我总结出了这份1071页Spring全家桶核心笔记
我有一头小花驴
博客等级 码龄6年 2553粉丝 · 2035原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值