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来实现优雅关闭,这块可以封装一下,让业务方引入依赖即可

都看到这了,不点个关注吗?

rocketmq优雅停机往事的更多相关文章
- JAVA优雅停机的实现
最近在项目中需要写一个数据转换引擎服务,每过5分钟同步一次数据.具体实现是启动engine server后会初始化一个ScheduledExecutorService和一个ThreadPoolExec ...
- dubbo-2.5.6优雅停机研究
不优雅的停机: 当进程存在正在运行的线程时,如果直接执行kill -9 pid时,那么这个正在执行的线程被中断,就好像一个机器运行中突然遭遇断电的情况,所导致的结果是造成服务调用的消费端报错,也有可能 ...
- 哦,这就是java的优雅停机?(实现及原理)
优雅停机? 这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的 ...
- Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题
Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...
- spring cloud shutdown graceful 优雅停机
spring cloud shutdown graceful 优雅停机 当一个服务启动后,会注册到eureka中,其他的服务也可以从eureka获取到新注册的服务.但当我们要停止一个服务的时候,如果直 ...
- dubbo之优雅停机
优雅停机 Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才 ...
- Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现
Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现 Anoyi 精讲JAVA 精讲JAVA 微信号 toooooooozi 功能介绍 讲解java深层次 ...
- ShutdownHook- Java 优雅停机解决方案
想象一下,如果你现在刚好在 word 上写需求文档,电脑突然重启.等待开机完成,你可能会发现写了一个小时文档没有保存,就这么没了... 一个正在运行 Java 应用如果突然将其停止,影响不止数据丢失, ...
- spring cloud 优雅停机
spring cloud 优雅停机 大部分部署项目如果要停掉项目一般都是用kill -9 来杀进程 但是由于Eureka采用心跳的机制来上下线服务,会导致服务消费者调用已经kill的服务提供者然后出错 ...
随机推荐
- 一文读懂Base64编码
Base64编码 字符对应表 上表就是用来表示Base64,一共64个字符,A-Z,a-z,0-9,+,-,还有=(作为补位) 无论将文件,字符串,还是什么转为Base64,一定是用上表的字符表示. ...
- Django——Ajax发送请求验证用户名是否被注册
场景: 用户注册的时候,输入用户名之后,Ajax发送请求到后端,后端验证该用户名是否已经被注册,然后返回到注册页面提示用户. 1.模型: from django.db import models cl ...
- Python - typing 模块 —— Union
前言 typing 是在 python 3.5 才有的模块 前置学习 Python 类型提示:https://www.cnblogs.com/poloyy/p/15145380.html 常用类型提示 ...
- 任由文字肆意流淌,更自由的开源 Markdown 编辑器
对于创作平台来说内容编辑器是十分重要的功能,强大的编辑器可以让创作者专注于创作"笔"下生花.而最好取悦程序员创作者的方法之一就是支持 Markdown 写作,因为大多数程序员都是用 ...
- C# 反射 + Quartz,实现流程处理
场景: 前不久,公司里项目经理要求我实现流程处理,比如,用户可以定义一个定时任务,每周一查看报表.定时任务很简单,用Quartz可以实现,但是用户自己选择报表就比较麻烦,因为系统的不同模块的生成报表的 ...
- centos8安装php扩展memcached报错
在php扩展memcached中执行./configure --with-php-config=/usr/local/php/bin/php-config 报错: error: memcached s ...
- Zookeeper分布式安装部署
1. 解压安装 1.1 解压Zookeeper安装包到/opt/module/目录下 tar -zxvf /opt/software/apache-zookeeper-3.6.2-bin.tar.gz ...
- Docker系列(18)- 具名挂载和匿名挂载
容器数据卷挂载方式 容器的数据卷可以看成就是容器的挂载方式:一个宿主机有多个容器,多个容器挂载方式不同,因此宿主机就有多个卷 每一个挂载方式在宿主机上都有一个名称,即卷名 宿主机如何查看这些卷,对使用 ...
- 《使用Jmeter进行批量发送http请求》
本文主要针对批量接口发送数据 一:接口测试的环境准备 1:JDK的安装:网上下载即可>1.6.0版本以上 2:jemeter工具的下载 (免安装):网上下载即可 3:插件的下载安装地址:http ...
- js运行机制 值引用 值传递
1.js是单线程的 为什么是单线程的呢 因为js作为浏览器脚本语言,会有很多和用户的互动,以及操作dom,多个线程会出问题. 2.js有同步任务,异步任务(ajax,用户点击等,settimeou ...