解决了这次的消息队列堆积事故,我又解锁了新的认知与思考...
大家好,我是程序员陶朱公。
前言
前两天解决了一个线上消息队列堆积事故,在这里做一个复盘与总结,希望我解决问题的方式、方法、手段对你将来遇到类似的事情有一定的帮助与启发。
背景
前两天,有业务方反馈,他们日常处理的工单数据变少了,希望开发同学去排查一下原因。
这里简单的画下我们系统的一个逻辑架构图:
在消费者应用前,有一个专门拉取邮件的服务,拉取邮件收件箱的邮件后,会将邮件存储在本地数据库作为原始邮件进行保存,保存成功后,会将保存下来的messageId,发送到MQ中去,供下游消费者应用消费。
像之前功能正常,消费者应用会从MQ中解析拿到MessageId,然后解析这封邮件,生成对应的工单。但一旦消费者应用由于各种原因消费不动了之后,就会造成队列消息的堆积。
分析
既然说到了堆积,我们就需要了解一下,堆积的整体情况:
可能有小伙伴觉得说,这个对接的数字不是很大呀,业务方这么敏感吗。这里我想简单表达一下,敏感度这个事情主要还是看具体业务,case by case来分析。像我们的业务,相关操作人员每天都要及时处理邮箱里客户反馈的内容,需要及时与客户暂开沟通,而且每一天对这个工单的处理量,也是他们的考核指标,如果由于系统原因,导致他们不能及时处理本该生成的工单,影响面很大。
OK既然是消息对接,如果这方面实战经验比较欠缺,但八股文背的苦瓜烂熟的小伙伴,第一意识可能从脑海中会蹦出来一个认知,是不是消费者应用自身性能问题,比如CPU100%啊、FULLGC啊等造成消费者线程没有资源进行调度、处理。
这个认知肯定是没错的,我第一时间,也是照这这个思路去看了一下线上监控大图,观察后发现,上述关心的一些列指标在线上都是正常不过的,CPU负载不高,内存够用,没有FULLGC等情况发生。
那还能是什么原因造成堆积了呢?如果是你,此刻,你会怎么继续排查呢,欢迎大家把各自的想法或思路,在评论区输出参与讨论。
这里分享一下我的排查思路。
其实也是很容易就联想到的即一定是固定数量的消费者线程不明所以被Hang死了,而且是出不来的那种,导致没办法继续处理队列里的数据。
上图是我们配置的消费者线程参数,用了两个线程来消费队列里的数据。如果两个线程都Hang死了,那绝B是会造成队列堆积的。
所以,我们有必要去堆栈里面,看看这两个消费者线程的整体情况。
怎么看呢?
有很多手段,比如jstack 线程ID或借助一些开源工具,比如阿里的Arthas。可以用Arthas列出所有线程
然后找出你的消费者线程ID,最后执行thread id就可以查看线程的堆栈情况。
当然如果你们的PASS平台或一些云应用,查看对接的线程,会更方便,像阿里云平台上就可以直接查看:
点击堆栈查看按钮后,我们按下里面的内容长啥样:
我截图的内容,正式消费者线程内部情况的表现。很明显,我框的类的方法有问题,导致了线程一直Hang在了那里出不来。后来定位到了相关代码,发现内部其实做了一件事情,就是将一个URL地址,一个在线的PDF地址,然后在pdfToPng方法内部,会流的形式解析生成本地图片进行保存,遇到一些特别大的PDF源文件,就长时间出不来了。
而且我也看了原来的代码,这代码URL链接出,居然都没设置超时时间这一个选项,如果遇到长时间的这种IO操作,那消费者线程不Hang死才怪。
解决方案
既然知道了问题的症结,那就不难处理,就像上图所示,在pdfTopng方法内部,加上超时时间设置,后来用几天时间观察了一下线上的数据,发现还是有效的。
结论
今天跟大家分享了一下,我前端时间遇到的一个线上消息队列堆积的事故,也跟大家详细阐述了,我遇到问题一步一步是如何分析、排查问题的,最后也给出了我修复问题的解决方案,希望大家喜欢。
写到最后
↓推荐关注↓