当前位置: 首页 > 科技观察

Rocketmq优雅宕机

时间:2023-03-19 13:39:51 科技观察

本文转载自微信公众号《捉虫大师》,作者为捉虫大师。转载本文请联系捕虫大师公众号。1时间追溯到2018年12月的某个晚上,那天我准备做完一个需求就上线回家。我一按释放键,警报就响了。二、确定只是偶然,不要惊慌。送完剩下的机器,又出现了几个同样的错误。作为一个优秀的(咸)秀(鱼)程序员,这种问题一定要追究到底。2熟练查询错误日志org.apache.ibatis.exceptions.PersistenceException:###Errorqueryingdatabase.Cause:org.springframework.jdbc.CannotGetJdbcConnectionException:CouldnotgetJDBCConnection;nestedexceptioniscom.alibaba.druid.readpool.DataSourceClosedException:cSourcedal看异常信息落陷入沉思从表面上看,报错似乎是因为使用了已经关闭的数据源。什么时候关闭数据源?只有进程被kill掉,是不是因为应用关闭的时候不够流畅?发布的时候会先去掉流量,应该不会至于天气,已经很晚了,漫无目的地拖着日志,疲惫地寻找新的线索,突然报错日志里一个字映入眼帘:“rocketmq”精神饱满,大概知道原因了。这个应用中还有一个辛苦的rocketmqconsumer一直在消费消息。当应用关闭时,外部流量被移除,但是没有人通知rocketmq消费者,所以抛出异常。3由于本人对rocketmq了解不深,甚至有点肤浅,其消费采用的是ack方式。如果报错,稍后会重试消息,不会丢失消息,而且如果消费代码是幂等的,就不会出现业务异常,不过反正无所谓,因为不是我的代码任何一个。看一眼消费者的代码(这里就不贴代码了,反正你也不会看),消费者注册了一个ShutdownHook,在ShutdownHook中消费者执行shutdown优雅退出,并为此设置最高优先级shutdownThread,但是从实用的角度来看,这个线程的最高优先级是没有用的。而从?一文中我们也知道ShutdownHook是并发执行的,spring容器的关闭也是一个ShutdownHook,它们之间没有先后顺序。明白了原因后,立马想到了类似dubbo的流抽取的方案。我写了一个优雅关闭rocketmqcosnumer的接口,在应用关闭脚本kill之前调用这个接口,完美解决了问题。猝死。4晚上睡着了,梦见老板让我改所有系统,吓得我魂飞魄散。所以第二天我重新考虑了这个问题。一直觉得在应用中实现一个接口,在stop脚本中调用是一件很不优雅的事情。更重要的是,这个不能复制到其他项目中,我陷入了另一种沉思。既然问题是spring容器关闭时bean的销毁顺序造成的,请问能不能用spring的depend-on来理顺顺序?去做就对了。一开始遇到这样的依赖关系:将xml中各个bean中的依赖关系手拉手配对好像起到了作用。但是当我打开第二个项目的时候,它的bean之间的依赖关系大致如下:好家伙,26个字母都快不够用了,我当时的心情就是这样的,所以我觉得按照现在的速度,所有的项目都会transformed可能一路到9102。5过了一会儿,突然在github交友网站上看到rocketmq官方实现的spring-boot-starter,于是点进去看它的实现。好家伙,看完直接打666。官方的starter实现了spring的SmartLifecycle接口。它的start方法可以在所有bean初始化完成后调用,stop方法会在bean销毁前调用,非常适合rocketmqconsumer。顺便也复习了一下spring容器的关闭。代码在AbstractApplicationContext的doClose方法中。这里我总结成一张图:从上图可以看出,在销毁bean之前,有两个动作:关闭生命周期bean和发送ContextClosedEvent。官方starter选择了LifeCycle接口的实现方式。6我要向这里的老板汇报。rocketmqconsumer发布的时候之所以不流畅,是因为我们的使用姿势。虽然对业务没有影响,但是也不优雅。有两种解决方法。老大,你可以选择:全部换成官方启动器依赖spring-boot,官方维护,改造成本很高。监听ContextClosedEvent,实现优雅关机。这个可以封装,让业务方引入依赖。