关于Spring Cloud服务优雅关闭的方案有很多种了,这里介绍一下使用kill
命令优雅关闭的方案,并解决会出现的问题。
所谓的优雅指两方面,一是程序在退出时要主动向Eureka取消注册自己,二是完成资源清理工作。比如我的程序里用到了线程池来异步执行一些任务,如果退出时不做清理,那么就有异步任务被异常中断导致业务数据不一致的风险。首先我们不能使用kill -9
。如果加了-9
,那么系统就不会给JVM调用 shutdown hook 的机会,也就无法完成资源清理了。
退出取消Euerka注册隐藏的坑
Spring Cloud默认的EurekaClientAutoConfiguration
这个自动配置类已经为我们做好了相应的工作,但是却不够完美。在程序收到kill信号时,JVM会调用 shutdown hook, 虽然在此hook中就有取消注册的逻辑,但我在实践中经常会遇到取消注册耗时特别长,导致 hook 线程block, 进程长时间等待而不能退出。这就会有一个致命的问题,因为kill命令并不会等待目标进程退出才会返回,而是立刻返回,这就意味着kill执行完后你的JVM进程还在。如果出现 hook 线程卡住的情况,那就极有可能当你再次启动服务的时候,上一次服务还没有关闭,从而导致新服务启动失败(往往是因为端口被占用)。
通过追踪源代码,我发现取消注册的逻辑是在EurekaAutoServiceRegistration#onApplicationEvent()
方法中实现的,此方法响应Spring容器的ContextCloseEvent
,然后调用stop()
方法取消注册,耗时