rocketmq 优雅停机往事
1
时间追溯到 2018 年 12 月的某一天夜晚,那天我正准备上线一个需求完就回家,刚点下发布按钮,告警就响起,我擦,难道回不了家了?看着报错量只有一两个,断定只是偶发,稳住不要慌。
把剩下的机器发完,又出现了几个同样的错误,作为一名优(咸)秀(鱼)程序员,这种问题必须追查到底。
2
娴熟地查询到报错日志
org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed
看着异常信息,陷入了沉思
表面上看报错是因为使用了已经关闭的数据源
数据源什么时候会关闭呢?只有进程被杀死的时候
莫非是应用关闭时不够平滑?发布时会先摘除流量的呀,应该不至于呀
天色已经很晚,漫无目的地拖动日志,疲惫地寻找新线索,突然报错日志中一个单词引入眼帘:rocketmq
精神抖擞,大概知道原因了,这应用中还有个兢兢业业的 rocketmq consumer 一直在消费消息,在应用关闭时,外部流量被摘除了,但没人通知 rocketmq consumer,于是它抛异常了。
3
出于我对 rocketmq 不深刻甚至有点肤浅的理解,它的消费采用 ack 的方式,如果报错,消息稍后还会重试,不会丢消息,而且如果消费代码是幂等的,也不会有业务上的异常,总之这不重要,因为它也不是我写的代码。
瞅了一眼 consumer 的代码(这里就不贴代码了,反正贴了你也不会看),consumer 注册了一个 ShutdownHook,ShutdownHook 里 consumer 执行了 shutdown 来优雅地退出,并且给这个 shutdownThread 设置了最高优先级
,然而从实践看来,这个线程最高优先级并没有什么卵用。
而且从《ShutdownHook原理》这篇文章中也知道 ShutdownHook 是并发执行的,spring 容器关闭也是一个 ShutdownHook,他们之前没有先后顺序。
了解原因后,第一时间想到了类似 dubbo 摘流的方案,吭哧吭哧写了个优雅关闭 rocketmq cosnumer 的接口,在应用关闭脚本的 kill 之前调用该接口,完美解决问题,赶紧下班回家,不然要猝死了。
4
夜里入睡,梦到老板让我把所有的系统都改造掉,吓得我一机灵。
于是第二天又重新思考这个问题,总觉得在应用里实现一个接口并在 stop 脚本中去调用是一件非常不优雅的事,更重要的是这也没法复制到其他项目,我又陷入了沉思。
既然是 spring 容器关闭时 bean 的销毁顺序导致的问题,那么能不能利用 spring 的 depend-on 把顺序理顺了?说干就干。
起初我遇到是这样的依赖关系:
手把手在 xml 的每个 bean 中把 depend-on 关系都配上,似乎也起到了作用。
但当我打开第二个项目时,它的 bean 之间的依赖关系大致如下:
好家伙,26 个字母差点不够用,当时我的心情是这样的
所以我觉得以当前的速度,改造完所有项目可能都到 9102 年了。
5
又过了一段时间,在 github 交友网站上突然看到了 rocketmq 官方实现的 spring-boot-starter,于是点进去看了它的实现。好家伙,看完直呼 666。
官方 starter 实现了 spring 的SmartLifecycle
接口,它的 start 方法能在所有 bean 初始化完成后被调用,stop 方法会在 bean 被销毁前调用,对 rocketmq consumer 来说简直完美。
顺便还复习了一下 spring 容器的关闭,代码在 AbstractApplicationContext 的 doClose 方法,这里我总结成一幅图:
通过上图能看到,销毁 bean 之前,有关闭 lifecycle bean 和发送 ContextClosedEvent 两个动作,官方 starter 选择了实现 LifeCycle 接口的方式。
6
到这里我该给老板汇报去了,之所以 rocketmq consumer 发布时不平滑是我们的使用姿势问题,虽然对业务没影响,但不优雅,解决方案有两个,老板你选吧:
全都换成官方 starter,依赖 spring-boot,官方维护,改造成本很高,
监听 ContextClosedEvent 来实现优雅关闭,这块可以封装一下,让业务方引入依赖即可
都看到这了,不点个关注吗?
版权声明: 本文为 InfoQ 作者【捉虫大师】的原创文章。
原文链接:【http://xie.infoq.cn/article/9e95922b572654856d9f7eefa】。文章转载请联系作者。
评论