早上 11 点 32 分,钉钉群突然被告警刷屏——订单支付成功消息的 MQ 队列深度从不到 1 万条,十分钟内飙到了 30 万。我扔下刚打开的早餐盒去翻监控,当时以为只是某个消费者实例挂掉了,心里还想着“重启一下就能解决”。结果短短一个小时,积压量冲到了 150 万,整个交易链路的支付回调、积分发放、消息通知全部被拖慢,连带着数据库连接池也开始报警。
这篇文章就记录这次生产环境 MQ 队列积压 150W 的完整排查过程与解决方案。里面没有玄学,只有一步步的监控比对、日志分析、线程栈定位和最终根因确认。如果你也负责生产环境的中间件运维,或者正在被“消费者明明活着、消息就是消费不下去”这类问题折磨,这篇文章应该能给你一些可落地的排查思路。
1. 故障初现:监控面板上的数据让我后背发凉
1.1 告警是怎么报出来的
我们的监控体系用的是 Prometheus + Alertmanager,RabbitMQ 的 messages 指标做了自定义采集,默认阈值是队列深度超过 5 万持续 5 分钟就触发 P1 告警。那天 11:32 的告警消息弹出来的时候,我第一反应是“是不是又有人手动往队列里塞测试数据了”,因为上一个版本刚上线了一个批量重发功能,专门用来处理历史脏消息。
但打开 Grafana 面板我就知道情况不对。订单支付成功队列的 messages_ready(等待被消费的消息数)曲线几乎是垂直拉升的,从 9,000 多一路冲到了 30 万,而且完全没有回头的意思。旁边另一个核心业务队列虽然也在涨,但斜率明显低很多。也就是说,这不是整体流量突增导致的普遍性排队,而是某一个消费链路出了问题。
1.2 我最初的三个猜测与快速验证
遇到队列积压,我脑子里冒出来的无非就三种常见情况:
- 消费者实例批量宕机,或者触发了优雅停机逻辑但没有正常消费完毕;
- 消费逻辑里某段代码出现异常,导致消息一直 ack 失败,陷入无限重试;
- 消息体解析出问题或下游依赖的某个接口超时,消费线程被卡死。
我先去看了消费者服务的实例数量和健康检查状态,Spring Boot 的 Actuator 显示 8 个实例全部 UP,JVM 内存和 CPU 也都在正常范围。这排除了第一种情况。接着我又去翻消费者日志,结果刚打开就发现了一大片 Retrying to consume message, attempt 3/5 之类的日志,以及大量 Connection timed out 异常。
到这里基本可以锁定问题不在 RabbitMQ 本身,而是消费者处理消息的时候,某个外部依赖变慢了。但到底是什么依赖、为什么变慢,还得继续往下挖。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 顺着消费链路逐层定位:从队列到线程再到下游依赖
2.1 第一眼:消息是堆积在待消费队列还是未确认区
排查 RabbitMQ 队列积压,第一步一定要分清 messages_ready 和 messages_unacknowledged 的区别。ready 表示消息还在队列里等着被投递给消费者;unacknowledged 表示消息已经投递出去了,但消费者还没回复 ack。这两个数字代表完全不同的故障类型。
我执行了 RabbitMQ 管理命令:
bash复制rabbitmqctl list_queues name messages messages_ready messages_unacknowledged consumers
输出结果大概是这样:
text复制order.payment.success 1502381 1000083 502298 16
这个数据非常关键。ready 有 100 万,说明积压的大部分消息根本没被消费到;但 unacknowledged 也有 50 万,而且 consumers 显示 16 个(8 个实例 × 2 个并发消费者),说明有大量消息已经投递给消费者了,但消费者的处理线程全部卡住了,根本没有回 ack。
这个现象基本推翻了“消费者宕机”的假设
