1. 背景
一个系统会有许多的中间件组成,包括 RPC(Consumer&Producer),Http,Job,MQ(Consumer&Producer),当CI/CD流水线运行,实例机器被 下线时,如果Dubbo Consumer组件的线程组先被回收后,还可以继续不停消费业务消息,消费消息时会使用Dubbo Consumer组件去调用其他服务,此时 Dubbo Consumer的线程组已经被回收了,就会导致数据消费时异常。如果没有消费重试的机制,可以当这些下线时的消息丢失了。
2. 方案
首先收到kill -9 或是 kill -15 的信号后,
- 切断数据源头: Http, Dubbo, MQ consumer
- 不再提交新的任务 Job ThreadPool
- 等待 其他组件(Mq Sender,Rest Client,Dubbo Consumer,Transaction )的线程执行完成
3. API
shell
# 可以添加Jvm自带的钩子方法
Runtime.addShutdownHook()
# 对于Spring容器的应用,可以监听close event 事件来,进行组件的优雅下线。4. 参考文献
4.1. 为什么难做
优雅上下线难点不在“收到退出信号”,而在于退出时应用内部有很多异步组件还在工作:
- HTTP 请求还在处理
- MQ 还在消费
- RPC 客户端还在调用下游
- 线程池里还有排队任务
如果处理顺序不对,就很容易造成消息丢失、半处理状态或下游调用失败。
4.2. 推荐顺序
一个更稳妥的顺序通常是:
- 先摘流量入口。
- 停止继续接收新消息和新任务。
- 等待存量任务处理完成。
- 最后再释放线程池、连接池和中间件客户端。
4.3. 实现关注点
- 退出等待时间要有上限,不能无限阻塞。
- MQ 消费位点提交和业务处理完成要配套设计。
- 定时任务、异步线程池和 RPC 客户端要统一纳入生命周期管理。