1.问题背景:
我们项目里用到了dubbo分布式框架,使用了redis注册中心,当服务shutdown时,没有向注册中心注销服务,也没有向consumer unregister;导致在服务重启后,第一次连接继续连接老的服务ip上并无法连接;
2.问题原因:
①阿里云的托管k8s集群当时还不支持shutdown hook,以及shutdown gracefully;
②kubelet发送kill命令(TERM signal)给容器类的1号进程,如果你运行的程序在别的进程上,那么就接受不到kill命令。
③启动服务使用 $ sh script.sh
命令在sh脚本中直接用 java -jar
命令启动,导致java进程不是1号进程。
3.解决办法:
①切换为支持shutdown gracefully的k8s集群
①还是使用 $ sh script.sh
方式启动程序,不过启动命令改为 exec java -jar
,让java程序运行在1号进程。
4.知识点:
①Linux kill命令
Linux kill命令用于删除执行中的程序或工作;
kill [-s <信息名称或编号>][程序PID] 或 kill [-l <信息编号>]
执行kill(默认kill -15)命令,系统会发送一个SIGTERM信号给对应的程序。当程序接收到该signal信号后,一般会先释放资源,然后自行停止进程;当然也可能最终阻塞住没有结束;
执行kill -9 命令,系统给对应程序发送的信号是SIGKILL,即exit。会强制终结进程,不会有任何其他操作;
②Kubernetes优雅停机策略
Kubernetes在终结pods的时候,默认会向每个container的1号进程发送TERM signal,优雅停机默认会容许30秒的停机周期;于此同时负载均衡也会将正在终结的pod从Endpoint中移除,这样就不会有新的流量流进将会被移除的pod
③shell,exec,source的区别
$ sh script.sh
执行脚本时,当前shell是父进程,生成一个子shell进程,在子shell中执行脚本。脚本执行完毕,退出子shell,回到当前shell
$ source script.sh
方式,在当前上下文中执行脚本,不会生成新的进程。脚本执行完毕,回到当前shell
exec command
方式,会用command进程替换当前shell进程,并且保持PID不变。执行完毕,直接退出,不回到之前的shell环境。