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优雅停机往事的更多相关文章

  1. JAVA优雅停机的实现

    最近在项目中需要写一个数据转换引擎服务,每过5分钟同步一次数据.具体实现是启动engine server后会初始化一个ScheduledExecutorService和一个ThreadPoolExec ...

  2. dubbo-2.5.6优雅停机研究

    不优雅的停机: 当进程存在正在运行的线程时,如果直接执行kill -9 pid时,那么这个正在执行的线程被中断,就好像一个机器运行中突然遭遇断电的情况,所导致的结果是造成服务调用的消费端报错,也有可能 ...

  3. 哦,这就是java的优雅停机?(实现及原理)

    优雅停机? 这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的 ...

  4. Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题

    Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...

  5. spring cloud shutdown graceful 优雅停机

    spring cloud shutdown graceful 优雅停机 当一个服务启动后,会注册到eureka中,其他的服务也可以从eureka获取到新注册的服务.但当我们要停止一个服务的时候,如果直 ...

  6. dubbo之优雅停机

    优雅停机 Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才 ...

  7. Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现

    Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现 Anoyi 精讲JAVA 精讲JAVA 微信号 toooooooozi 功能介绍 讲解java深层次 ...

  8. ShutdownHook- Java 优雅停机解决方案

    想象一下,如果你现在刚好在 word 上写需求文档,电脑突然重启.等待开机完成,你可能会发现写了一个小时文档没有保存,就这么没了... 一个正在运行 Java 应用如果突然将其停止,影响不止数据丢失, ...

  9. spring cloud 优雅停机

    spring cloud 优雅停机 大部分部署项目如果要停掉项目一般都是用kill -9 来杀进程 但是由于Eureka采用心跳的机制来上下线服务,会导致服务消费者调用已经kill的服务提供者然后出错 ...

随机推荐

  1. Java最大栈深度有多大?-从一道面试题开始学习JVM

    一.问题:Java最大支持栈深度有多大? 1.分析 有JVM的内存结构我们可知: 随着线程栈的大小越大,能够支持越多的方法调用,也即是能够存储更多的栈帧: 局部变量表内容越多,那么栈帧就越大,栈深度就 ...

  2. java设计模式—单例模式(包含单例的破坏)

    什么是单例模式? 保证一个了类仅有一个实例,并提供一个访问它的全局访问点. 单例模式的应用场景? 网站的计数器,一般也是采用单例模式实现,否则难以同步: Web应用的配置对象的读取,一般也应用单例模式 ...

  3. 创建 Spring容器的三种方式

    一.src路径下打包完在war包的classes层级下 1.Spring容器创建的三种方式 创建Bean容器之后创建对象: 其中第三种使用的是BeanFactory对象 2.spring通过配置文件用 ...

  4. 【第十八篇】- Maven Eclipse之Spring Cloud直播商城 b2b2c电子商务技术总结

    Maven Eclipse Eclipse 提供了一个很好的插件 m2eclipse ,该插件能将 Maven 和 Eclipse 集成在一起. 在最新的 Eclipse 中自带了 Maven,我们打 ...

  5. RMQ区间最值查询

    RMQ区间最值查询 概述 RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A, 回答若干询问RMQ(A,i,j)(i,j<= ...

  6. python类、对象

    python类.对象 学习完本篇,你将会深入掌握 什么是类,对象 了解类和对象之间的关系 能独立创建一个People(人类),属性有姓名.性别.年龄.提供一个有参的构造方法,编写一个show方法,输出 ...

  7. python循环以及控制语句

    python流程 学习完本篇,你将会通过python完成以下题目 试利用break语句求解2-100之间的素数. (1)素数是指除了能被1和它本身整除外,不能被其它数所整除的数.判断一个自然数是否是素 ...

  8. 一起搞懂PHP的错误和异常(一)

    在PHP的学习过程中,我们会接触到两个概念,一个是错误,一个是异常.啥玩意?他们不是一个东西嘛?如果接触过Java.C#之类的纯面向对象语言的同学,可能对异常是没有什么问题,毕竟所有的问题都可以try ...

  9. mybatis if else if 条件判断SQL片段表达式取值和拼接

    前言 最近在开发项目的时候涉及到复杂的动态条件查询,但是mybaits本身不支持if elseif类似的判断但是我们可以间接通过 chose when otherwise 去实现其中choose为一个 ...

  10. centos 7 部署 zookeeper

    Zookeeper集群搭建 由于公司缓存方案改进,准备采用codis集群作为主要的缓存解决方案(codis:国内豌豆荚开发的redis集群解决方案,已开源,github地址:https://githu ...