线上 MQ 突然积压几百万条消息,监控一片红,老板在旁边盯着,这时候最忌讳什么?上来就翻日志查原因。你查半小时,业务就挂了半小时,这账怎么算都不划算。
处理积压的正确姿势就九个字:先止血、再排查、后预防。优先级永远是先把业务救回来,再谈定位问题。
先说止血,十分钟内必须完成。
第一招,优先扩容消费端。这里有个硬限制你得知道:Kafka 和 RocketMQ 里,一个分区同一时间只能被一个消费线程消费,所以消费线程数最多等于分区数。操作上就是先临时加消费组实例数,再把单个实例的消费线程池核心数调大。大促的时候,我们 16 个读队列只起了 4 个消费者,扩到 16 台,消费速度当场翻倍。
第二招,降级非核心业务。日志、统计、推送这些非核心消息的消费直接关掉,把 CPU、内存、数据库连接全让给核心业务。电商大促时先停掉用户行为分析、商品推荐的消息消费,属于常规操作。
第三招,消息临时转储。积压量到百万级以上,直接消费会把 MQ 集群拖垮。写个简单脚本,把消息先转存到 Redis 或 MySQL,业务高峰过了再慢慢回放。
第四招,跳过死信消息。大量重复失败的死信会阻塞正常消费,先临时跳过,事后再单独处理。
![]()
血止住了,业务恢复了,这时候才轮到排查根因。90% 以上的积压问题出在消费端,不是生产端。常见就那么几类:消费端慢 SQL 卡死、第三方接口超时设置过长(比如 30 秒)、线程池队列过长导致任务堆在内存里,还有数据库连接池被耗尽,所有消费线程都在干等。
![]()
排查工具方面,Arthas trace 直接抓消费链路的耗时分布,一抓一个准。我当时就发现消费一条消息要 3 秒,全是卡在调下游优惠券接口上,P99 超时严重。后来上游加了一层本地缓存加降级兜底,接口挂了直接走缓存或默认券,单条消息处理从 3 秒降到 20 毫秒,积压很快就消下去了。
代码层面有几个技术点值得说。批量消费:一次拉取 32 条甚至 64 条消息,减少网络往返;虚拟线程:JDK 21 的虚拟线程做消费线程池,并发能力大幅提升;幂等性:用 Redis 的 SETNX 加过期时间实现,比数据库唯一索引快十倍以上。
还有个容易忽略的坑——顺序消息。直接增加读写队列会导致同一个 key 的消息被分到不同队列,顺序全乱。一般做法是暂停非顺序消费,把资源全部倾斜给顺序队列,或者按业务 key 水平拆分、增加分区数,但必须保证同一个 key 永远进同一个队列。真到了队列打满、消费者加不进去的地步,可以建一个临时的"泄洪 Topic",把积压消息快速搬过去,用一批临时机器全力消费,跟原业务完全隔离。
![]()
最后说预防。大促前一定要压测消费链路,很多团队只压生产端,结果消费链路被拖垮,一翻车就是大事故。另外积压量要实时告警,超过 1000 条普通告警,超过 10000 条紧急电话告警,短信、企业微信、电话多渠道通知,提前感知总比事后救火强。
说白了,MQ 积压这事,考验的不是你懂多少原理,而是紧急时刻能不能稳住节奏。先救业务,再查原因,最后补预防,这个顺序千万别搞反。你在项目里遇到过消息积压吗?当时是怎么处理的?评论区聊聊。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.