你的Kubernetes Java应用优雅停机了吗?
假如我们从 kafka 拉取数据然后生成任务处理数据,在服务退出时,如何保证内存中的数据能被正常处理完不丢失呢?假如服务是部署在 Kubernetes
中又该如何处理?
Java 应用优雅停机
我们首先考虑下,一般在什么场景下数据会丢失呢?
- 升级服务时
- pod重启时
- 服务器断电时
因为服务器断电属于极端情况,我们暂且不考虑。那就只有 Java 退出时我们要保证数据的完整性了。在 Java 中,有一个方法可以实现应用退出时候的优雅停机:shutdown hook
。Spring boot
把这个东西封装了一下,可以通过 @PreDestroy
注解实现。当 JVM
收到退出的信号时,会调用 shutdown hook
中的方法,完成清理操作。示例代码如下:
Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {System.out.println("Start to run shutdown hook.");}
})
Shutdown hook
可以保证在我们代码主动调用 System.exit()
, OOM
, 在终端执行 Ctrl+C
,以及应用主动关闭等情况下时被调用。在实际的场景中,我们可以在上述的线程中执行清理操作。比如,停止 kafka 的数据消费,以及任务的及时处理等。
当我们使用 java -jar *.jar
运行 Java
程序后,通过执行 kill $pid
,可以发现程序确实可以优雅退出。但是当我把服务部署到 Kubernetes
时,发现这个逻辑并没有被执行,到底哪里出了问题?
在 Kubernetes 中优雅停机
当我们发送 delete
命令给 pod
时,Kubernetes
会使用优雅停机(默认30s时间),在优雅停机过程中,此 pod
在 API server
中会被更新为dead
状态。当我们用kubectl
命令查看此pod
时,它被展示为Terminating
的状态。当 Kubelet
看到 pod
被标记为了 Terminating
状态时,它就会开始执行 pod
的 shutdown
程序。如果我们 pod 的容器定义了 preStop hook
,那么这个 hook 会在容器中执行;与此同时,Kubelet
会向容器内发送一个TERM
信号。Service
也会将此 pod 从 endpoint 列表移除。当优雅停机时间过后,在 pod
里仍然存活的进程则会被SIGKILL
命令杀掉。Kubelet
会在 API server
里通过设置 grace period=0
(立即删除)来完成 Pod 的删除操作。删除后此 Pod 会在API中消失,并且在客户端也不可见了。
以上,可以看出,我们的容器是会收到 TERM
信号的,按照常理,如果我们的 Java
进程收到了 TERM
信号是可以正常执行我们写的 shutdown hook
优雅退出的,但是这里却没有执行,很有可能是我们的 Java
进程根本就没有收到信号。
查看我们的 Dockerfile
,发现我们定义的启动命令是执行一个 run.sh
的脚本,在 run.sh
脚本中,进一步执行了启动 Java
进程的命令。
# run.sh
...
sh start.sh start
...
while [1]
do sleep 30
done
可以看到,我们在 run.sh
中进一步执行了 start.sh
,Java 进程的启动逻辑在start.sh
脚本中。我们可以执行 ps -ef
查看下当前容器中的进程
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 11:01 ? 00:00:00 bash ~/run.sh
root 4084 1 8 11:01 ? 00:15:00 java -Dname=test
root 14913 1 0 13:49 ? 00:00:00 sleep 30
root 14914 0 0 13:50 pts/0 00:00:00 bash
root 14955 14914 0 13:50 pts/0 00:00:00 ps -ef
可以看到,我们运行的 run.sh
的 PID 是 1
,Java 进程的 PID 是 4084,Java 进程是 run.sh
进程的一个子进程。问题就出在这里,在 pod 被删除时,TERM
信号只会发送给 1号
进程,而 run.sh
接收到此信号后并不会将其转发给 Java 进程,因此 Java 便无法触发 shutdown hook
,无法实现优雅退出。最终,Java 是被 SIGKILL
信号杀掉的(强制退出)。所以,我们只需要让 Java 进程作为 1号
进程就行了。改写下脚本,我们把启动 Java 进程的命令放到 run.sh
中
# run.sh
...
exec java $JAVA_OPTS -jar ./*.jar --server.port=8080
...
while [1]
do sleep 30
done
exec
的作用是被执行的命令行替换掉当前的 shell
进程。测试发现 OK,此时我们实现了优雅停机。但是,这足够优雅吗?
更优雅地停机
在上一步,我们实现了优雅停机,但是其实这并不是最优方案。我在看 start.sh
脚本中,发现此脚本定义了 start, restart, stop, status
4个方法,而且这个脚本中定义了很多额外的变量,如果我们要把之前的功能都实现的话,就需要把逻辑都搬到 run.sh
中。这无疑会增大工作量,这是不优雅的原因之一。
其次,一般是不推荐把 Java 进程
作为1号
进程的。因为在 Linux
中,1号
进程有特殊作用:1号
进程会作为孤儿进程的父进程,它需要对自己的子进程进行清理回收,避免系统产生僵尸进程。bash
可以很好地处理这种清理工作,我们一般自己写的 Java 程序是不会考虑这种东西的。
那么,就需要我们在 shell
中接收到 TERM
信号后把信号传递给 Java 进程了。这需要怎么做呢?我们需要使用trap
命令。trap
命令的作用是捕捉信号和其他事件并执行命令。
# run.sh
...
sh start.sh startgrace_exit() {echo 'grace exit started'sh start.sh stop &wait $!echo 'grace exit finished'
}
trap 'grace_exit' TERM INT
...
while [1]
do sleep 30
done
在脚本中,我们使用 trap
捕捉 TERM
(Kubelet
发送的信号) 和 INT
(快速关闭,当用户输入 Control-C
时由终端程序发送) 信号,捕捉到了以后,我们执行了 grace_exit
方法,在此方法中,调用了 start.sh
脚本的 stop
方法,其实这个 stop
方法就是找到了 Java 进程,然后给其发送了 kill 命令,我们直接在 grace_exit
中执行相同逻辑也是可以的,这里是为了复用逻辑。我们还使用了 &
保证 stop
方法在后台运行,这样方便我们获取其进程号($!
会返回shell
最后运行的后台进程的 PID),等待其执行结束。 这样,当我们 delete``pod
时,Kubelet
发送 TERM
信号后,我们就能传达给 Java 进程,进而让 Java 进程进行优雅停机了。
标题:你的Kubernetes Java应用优雅停机了吗?
作者:末日没有进行曲
链接:你的Kubernetes Java应用优雅停机了吗?
时间:2021-01-15
声明:本博客所有文章均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
你的Kubernetes Java应用优雅停机了吗?相关推荐
- 【Java】优雅停机时的一点思考
1.概述 转载:http://cxytiandi.com/blog/detail/15386 转载自:徐靖峰 Kirito的技术分享 最近瞥了一眼项目的重启脚本,发现运维一直在使用 kill -9 的 ...
- java停机保存数据_哦,这就是java的优雅停机?(实现及原理)
优雅停机?这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的请 ...
- java唱歌打分系统原理_哦,这就是java的优雅停机?(实现及原理)
优雅停机?这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的请 ...
- Java应用的优雅停机
一. 优雅停机的概念 优雅停机一直是一个非常严谨的话题,但由于其仅仅存在于重启.下线这样的部署阶段,导致很多人忽视了它的重要性,但没有它,你永远不能得到一个完整的应用生命周期,永远会对系统的健壮性持怀 ...
- 95-22-010-停止-优雅停机
文章目录 1.优雅停机 2. 调用方法 1.优雅停机 Netty的优雅停机三部曲: 不再接收新消息 退出前的预处理操作 资源的释放操作 Java的优雅停机通常通过注册JDK的ShutdownHoo ...
- dubbo protocol port 消费者端_Dubbo 优雅停机演进之路
一.前言 在 『ShutdownHook- Java 优雅停机解决方案』 一文中我们聊到了 Java 实现优雅停机原理.接下来我们就跟根据上面知识点,深入 Dubbo 内部,去了解一下 Dubbo 如 ...
- 微服务架构—优雅停机方案
1 介绍 微服务架构中的应用优雅停机主要是指应用实例有计划而平滑(即不产生需要处理的事故)的退出.应用服务器的停机主要分为两类:主动停机和被动停机,而其中主动停机和大部分的被动停机都是可以实现优雅停机 ...
- 中台化实践——优雅停机方案
1 介绍 微服务架构中的应用优雅停机主要是指应用实例有计划而平滑(即不产生需要处理的事故)的退出.应用服务器的停机主要分为两类:主动停机和被动停机,而其中主动停机和大部分的被动停机都是可以实现优雅停机 ...
- java signal handler_JAVA优雅停机的实现
最近在项目中需要写一个数据转换引擎服务,每过5分钟同步一次数据.具体实现是启动engine server后会初始化一个ScheduledExecutorService和一个ThreadPoolExec ...
最新文章
- tomcat 启动报栈溢出 解决方法
- java word表格_Java 添加Word表格行或列
- @PropertySource@ImportResource@Bean
- 手把手教你在Linux上搭建BitTorrent服务器
- 五十种巧妙优化SQL Server数据库
- Nacos源码HostReactor
- NWERC 2018——B.Brexit Negotiations
- 6个步骤卸载wine
- Java类class cast()方法及示例
- 我就是互联网的老不死
- 8.5. JdbcTemplate
- 同步异步线程进程的一些思考
- grub2 引导光盘
- 计算机网络技术提纲,计算机网络技术复习提纲
- java实现车牌头像识别_LPR java车牌图像处理 输入一个车牌照片(不是整车的照片) - 下载 - 搜珍网...
- windows11百度网盘下载,win11iso镜像百度云下载
- Solidity函数中pure、view、constant的用法
- 中文分词算法python_简单的中文分词算法
- 密西根大学张阳教授受聘中国上海交通大学客座教授(图)
- W800/W801学习记录网络部分(一):WIFI的扫描和连接
热门文章
- linux nfs root无权限,nfs root 无权限_如何允许root用户访问NFS?
- SSM+Java体育用品库存管理系统 毕业设计-附源码211712
- 直播软件源码如何在Android端实现多人视频通话
- 麦块我的世界怎么用java_我的世界从进入游戏到多人游戏 生存要点 Java下载一套龙教程【含麦块使用教程】...
- Tomcat在指定JDK版本启动
- 黑客:iPhone的这些脑残功能逼我去越狱 苹果:乖,招安是我强项!
- HTML 为元素设置边框
- 最好用的17个安全漏洞检查工具
- Stata:无条件分位数回归及应用
- 还原android系统文件夹,如何从Android的内存中恢复文件-万兴恢复专家