网易首页 > 网易号 > 正文 申请入驻

拒绝 kill -9 强制停止,如何优雅停止 Spring Boot 服务?

0
分享至

>>号外:关注“Java精选”公众号,回复“2021面试题”,领取免费资料!“Java精选面试题”小程序,3000+ 道面试题在线刷,最新、最全 Java 面试题!

对于微服务来说,服务的优雅上下线是必要的。就上线来说,如果组件或者容器没有启动成功,就不应该对外暴露服务,对于下线来说,如果机器已经停机了,就应该保证服务已下线,如此可避免上游流量进入不健康的机器。

优雅下线

基础下线(Spring/SpringBoot/内置容器)

首先JVM本身是支持通过shutdownHook的方式优雅停机的。

Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
close();

此方式支持在以下几种场景优雅停机:

1)程序正常退出 2)使用System.exit() 3)终端使用Ctrl+C 4)使用Kill pid干掉进程

那么如果你偏偏要kill -9 程序肯定是不知所措的。

而在Springboot中,其实已经帮你实现好了一个shutdownHook,支持响应Ctrl+c或者kill -15 TERM信号。

随便启动一个应用,然后Ctrl+c一下,观察日志就可知,它在AnnotationConfigEmbeddedWebApplicationContext这个类中打印出了疑似Closing...的日志,真正的实现逻辑在其父类

AbstractApplicationContext中(这个其实是spring中的类,意味着什么呢,在spring中就支持了对优雅停机的扩展)。

public void registerShutdownHook() {
if (this.shutdownHook == null) {
this.shutdownHook = new Thread() {
public void run() {
synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
AbstractApplicationContext.this.doClose();

Runtime.getRuntime().addShutdownHook(this.shutdownHook);

public void destroy() {
this.close();
}

public void close() {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.doClose();
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
} catch (IllegalStateException var4) {
;
}
}

protected void doClose() {
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Closing " + this);
}

LiveBeansView.unregisterApplicationContext(this);

try {
this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
} catch (Throwable var3) {
this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
}

if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
} catch (Throwable var2) {
this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
}
}

this.destroyBeans();
this.closeBeanFactory();
this.onClose();
this.active.set(false);
}

}

我们能对它做些什么呢,其实很明显,在doClose方法中它发布了一个ContextClosedEvent的方法,不就是给我们扩展用的么。于是我们可以写个监听器监听ContextClosedEvent,在发生事件的时候做下线逻辑,对微服务来说即是从注册中心中注销掉服务。

@Component
public class GracefulShutdownListener implements ApplicationListener {

@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent){
//注销逻辑
zookeeperRegistry.unregister(mCurrentServiceURL);
...
}
}

可能会有疑问的是,微服务中一般来说,注销服务往往是优雅下线的第一步,接着才会执行停机操作,那么这个时候流量进来怎么办呢?

个人会建议是,在注销服务之后就可开启请求挡板拒绝流量了,通过微服务框架本身的故障转移功能去处理被拒绝的流量即可。

Docker中的下线

好有人说了,我用docker部署服务,支不支持优雅下线。

那来看看docker的一些停止命令都会干些啥:

一般来说,正常人可能会用docker stop或者docker kill 命令去关闭容器(当然如果上一步注册了USR2自定义信息,可能会通过docker exec kill -12去关闭)。

对于docker stop来说,它会发一个SIGTERM(kill -15 term信息)给容器的PID1进程,并且默认会等待10s,再发送一个SIGKILL(kill -9 信息)给PID1。

那么很明显,docker stop允许程序有个默认10s的反应时间去做一下优雅停机的操作,程序只要能对kill -15 信号做些反应就好了,如上一步描述。那么这是比较良好的方式。

当然如果shutdownHook方法执行了个50s,那肯定不优雅了。可以通过docker stop -t 加上等待时间。

外置容器的shutdown脚本(Jetty)

如果非要用外置容器方式部署(个人认为浪费资源并提升复杂度)。那么能不能优雅停机呢。

可以当然也是可以的,这里有两种方式:

首先RPC框架本身提供优雅上下线接口,以供调用来结束整个应用的生命周期,并且提供扩展点供开发者自定义服务下线自身的停机逻辑。同时调用该接口的操作会封装成一个preStop操作固化在jetty或者其他容器的shutdown脚本中,保证在容器停止之前先调用下线接口结束掉整个应用的生命周期。shutdown脚本中执行类发起下线服务 -> 关闭端口 -> 检查下线服务直至完成 -> 关闭容器的流程。

而更简单的另一种方法是直接在脚本中加入kill -15命令。

优雅上线

优雅上线相对来说可能会更加困难一些,因为没有什么默认的实现方式,但是总之呢,一个原则就是确保端口存在之后才上线服务。

springboot内置容器优雅上线

这个就很简单了,并且业界在应用层面的优雅上线均是在内置容器的前提下实现的,并且还可以配合一些列健康检查做文章。

参看sofa-boot的健康检查的源码,它会在程序启动的时候先对springboot的组件做一些健康检查,然后再对它自己搞得sofa的一些中间件做健康检查,整个健康检查的流程完毕之后(sofaboot 目前是没法对自身应用层面做健康检查的,它有写相关接口,但是写死了port is ready...)才会暴露服务或者说优雅上线,那么它健康检查的时机是什么时候呢:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
healthCheckerProcessor.init();
healthIndicatorProcessor.init();
afterHealthCheckCallbackProcessor.init();
publishBeforeHealthCheckEvent();
readinessHealthCheck();

可以看到它是监听了ContextRefreshedEvent这个事件。在内置容器模式中,内置容器模式的start方法是在refreshContext方法中,方法执行完成之后发布一个ContextRefreshedEvent事件,也就是说在监听到这个事件的时候,内置容器必然是启动成功了的。

但ContextRefreshedEvent这个事件,在一些特定场景中由于种种原因,ContextRefreshedEvent会被监听到多次,没有办法保证当前是最后一次event,从而正确执行优雅上线逻辑。

在springboot中还有一个更加靠后的事件,叫做ApplicationReadyEvent,它的发布藏在了afterRefresh还要后面的那一句listeners.finished(context, null)中,完完全全可以保证内置容器 端口已经存在了,所以我们可以监听这个事件去做优雅上线的逻辑,甚至可以把中间件相关的健康检查集成在这里。

@Component
public class GracefulStartupListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent){
//注册逻辑 优雅上线
apiRegister.register(urls);

外置容器(Jetty)优雅上线

目前大多数应用的部署模式不管是jetty部署模式还是docker部署模式(同样使用jetty镜像),本质上用的都是外置容器。那么这个情况就比较困难了,至少在应用层面无法观察到外部容器的运行状态,并且容器本身没有提供什么hook给你实现。

那么和优雅上线一样,需要RPC框架提供优雅上线接口来初始化整个应用的生命周期,并且提供扩展点给开发者供执行自定义的上线逻辑(上报版本探测信息等)。同样将调用这个接口封装成一个postStart操作,固化在jetty等外置容器的startup脚本中,保证应用在容器启动之后在上线。容器执行类似启动容器 -> 健康检查 -> 上线服务逻辑 -> 健康上线服务直至完成 的流程。

作者:fredalxin fredal.xin/graceful-soa-updown

往期精选 点击标题可跳转

点个赞,就知道你“在看”!

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
高盛:中国正在考虑进行全面的消费税改革

高盛:中国正在考虑进行全面的消费税改革

风向观察
2024-06-19 19:07:10
2天8个瓜,四字顶流塌房,黄晓明疑似恋爱,关晓彤被罚,出轨离婚

2天8个瓜,四字顶流塌房,黄晓明疑似恋爱,关晓彤被罚,出轨离婚

兮说戏说
2022-05-27 22:56:54
台湾新冠重症及死亡病例大幅增加

台湾新冠重症及死亡病例大幅增加

界面新闻
2024-06-19 10:40:22
中菲撞船事件后,美发出军事介入威胁,6国要求中国承认南海仲裁

中菲撞船事件后,美发出军事介入威胁,6国要求中国承认南海仲裁

大光观察
2024-06-19 18:54:50
32岁“瑞士梅西”无解世界波!创8大纪录:连6届大赛破门欧洲首人

32岁“瑞士梅西”无解世界波!创8大纪录:连6届大赛破门欧洲首人

我爱英超
2024-06-20 04:59:15
奥运女排死亡之组诞生!中国队幸运,保底小组第3,死磕卫冕冠军

奥运女排死亡之组诞生!中国队幸运,保底小组第3,死磕卫冕冠军

侃球熊弟
2024-06-19 19:28:40
余琦三个问题很严重,警方已经调查,老公身份曝光,结局大快人心

余琦三个问题很严重,警方已经调查,老公身份曝光,结局大快人心

兰子记
2024-06-19 18:42:11
世卫组织:将疫苗接种作为政治武器会带来严重后果

世卫组织:将疫苗接种作为政治武器会带来严重后果

新华社
2024-06-19 19:52:06
京剧国家二级女演员被丈夫举报长期出轨,超大尺度聊天记录曝光!

京剧国家二级女演员被丈夫举报长期出轨,超大尺度聊天记录曝光!

兵叔评说
2024-06-19 14:19:48
超级大冷!张之臻2-1抢七险胜NO.5梅德韦杰夫 晋级八强创5纪录

超级大冷!张之臻2-1抢七险胜NO.5梅德韦杰夫 晋级八强创5纪录

醉卧浮生
2024-06-20 00:48:14
正式上诉国际足联!有望重赛,国足18强对手存变数,球迷乐开花

正式上诉国际足联!有望重赛,国足18强对手存变数,球迷乐开花

十点体坛
2024-06-19 23:32:27
金正恩接机普京,细节满满,两人的修养藏不住

金正恩接机普京,细节满满,两人的修养藏不住

天择杂谈
2024-06-20 00:10:06
现场直击|胖东来“爆改”的永辉超市亮相:下架单品逾1万,员工月薪涨千元

现场直击|胖东来“爆改”的永辉超市亮相:下架单品逾1万,员工月薪涨千元

第一财经资讯
2024-06-19 17:14:42
上海地铁突发持刀伤人,54岁老头捅伤3人,还把刀架在自己脖子上

上海地铁突发持刀伤人,54岁老头捅伤3人,还把刀架在自己脖子上

社会酱
2024-06-19 18:00:14
《参考消息》品牌价值接近千亿元

《参考消息》品牌价值接近千亿元

参考消息
2024-06-19 19:52:10
以军批准对黎巴嫩真主党发动进攻的作战计划,会全面开战么?专家:以军兵力有限

以军批准对黎巴嫩真主党发动进攻的作战计划,会全面开战么?专家:以军兵力有限

红星新闻
2024-06-19 21:10:27
欧洲杯重大争议!VAR拒绝改判,东道主获利了,主裁判被球迷狂嘘

欧洲杯重大争议!VAR拒绝改判,东道主获利了,主裁判被球迷狂嘘

侃球熊弟
2024-06-20 00:37:43
黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

萌神木木
2024-06-18 21:18:32
波音“星际客机”飞船返回时间被推迟至6月26日

波音“星际客机”飞船返回时间被推迟至6月26日

环球网资讯
2024-06-19 06:00:11
曝蒙古国反华分子big gee来中国开演唱会,歌词:把中国人像猪一样杀

曝蒙古国反华分子big gee来中国开演唱会,歌词:把中国人像猪一样杀

不掉线电波
2024-06-19 16:53:13
2024-06-20 06:02:44
Java精选
Java精选
一场永远也演不完的戏
1551文章数 3855关注度
往期回顾 全部

科技要闻

618观察:谁为高强度的低价竞争买单?

头条要闻

欧洲杯:苏格兰1-1瑞士 沙奇里无解世界波

头条要闻

欧洲杯:苏格兰1-1瑞士 沙奇里无解世界波

体育要闻

欧洲杯最大的混子,非他莫属

娱乐要闻

黄一鸣“杀疯了” 直播间卖大葱养孩子

财经要闻

深化科创板改革 证监会发布八条措施

汽车要闻

双肾格栅变化大/内饰焕新 新一代宝马X3官图发布

态度原创

健康
房产
亲子
本地
公开课

晚餐不吃or吃七分饱,哪种更减肥?

房产要闻

17.9亿!终于,有民企在三亚大手笔拿地了!周边房价10万+!

亲子要闻

宝宝看到对面小朋友们在打篮球也跟着模仿动作有模有样。

本地新闻

中式沙拉宇宙的天花板,它必须有姓名

公开课

近视只是视力差?小心并发症

无障碍浏览 进入关怀版