6.2 常见问题与故障排除 本节摘要:八类高频故障按"现象、根因、定位、处置"四列组织,排查顺序遵循全册追踪单的帧序——消息死在哪一帧,就查哪一段。本节是深夜值班的急救卡:先对号入座,再按帧取证,最后对症处置。 值班夜里接到"消息队列出事了"的告警,最贵的动作是瞎猜。本节把前五章遇到过的所有故障形态收拢成手册,每一条的排查路径都标注了对应章节——急救时先翻表,再动手。 排查总纲:先定帧,再定因 任何消息类故障的第一问都是:消息死在哪一帧? 回顾 2.6 节的全流程:编码、通道发出、Broker 接收、交换机判决、入队、投递、签收。用管理界面看三个数——发布速率、队列堆积、消费速率——三个数的组合能直接把故障分到段。
本节摘要:八类高频故障按"现象、根因、定位、处置"四列组织,排查顺序遵循全册追踪单的帧序——消息死在哪一帧,就查哪一段。本节是深夜值班的急救卡:先对号入座,再按帧取证,最后对症处置。
值班夜里接到"消息队列出事了"的告警,最贵的动作是瞎猜。本节把前五章遇到过的所有故障形态收拢成手册,每一条的排查路径都标注了对应章节——急救时先翻表,再动手。
任何消息类故障的第一问都是:消息死在哪一帧? 回顾 2.6 节的全流程:编码、通道发出、Broker 接收、交换机判决、入队、投递、签收。用管理界面看三个数——发布速率、队列堆积、消费速率——三个数的组合能直接把故障分到段。分诊逻辑画成流程图,值班时按图走即可:

用管理界面看三个数时有个细节提醒:速率要看时间窗而不是瞬时值,界面里的消息速率图默认五秒刷新,抖动很大——把时间窗拉到五分钟以上再下判断,否则容易把正常毛刺读成故障。
| 类别 | 典型现象 | 根因 | 处置(详见章节) |
|---|---|---|---|
| 堆积型 | ready 持续增长 | 消费能力不足或消费端卡死 | 5.4 三段定位,紧急先扩消费者 |
| 蒸发型 | 队列与业务都不见消息 | 自动确认或未持久化 | 3.1、3.2,修复签收与三件套 |
| 拒连型 | 客户端连接被拒 | 认证失败或 vhost 错误 | 4.2 核对账户与 vhost |
| 参数冲突型 | 启动即通道报 406 | 声明参数漂移 | 2.4 迁移流程对齐参数 |
| 重复消费型 | 业务出现重复处理 | 重投递叠加无幂等 | 3.4 变式与 5.5 幂等表 |
| 顺序错乱型 | 状态机报错 | 多消费者或重投乱序 | 3.4 分片保序改造 |
| 资源阻塞型 | 发布时快时慢 | 内存或磁盘水位触发 | 4.1 参数与 4.4 告警处置 |
| 连接泄漏型 | 连接数缓涨直至拒绝服务 | 每任务建连不复用 | 2.5 单例化改造 |
故障一,堆积型。现象:ready 每分钟涨十万。定位三步:先看 consumers 列——为零就是消费者全体掉线,回 Connections 页查掉线原因(多半伴随 OOM 或网络割接);不为零再看消费速率与发布速率的剪刀差,速率接近说明只差容量,速率远低说明处理卡住;处理卡住的深挖用 5.4 节热区表——八成是回调里的慢操作。紧急处置按优先级:扩消费者实例(最快)、临时提高 prefetch(处理快时有效)、启用降级把非关键下游断开(6.1 案例一的预案)。禁止动作:删队列——那等于把事故升级成数据事故。
故障二,蒸发型。现象:业务说没收到,队列里也查无此消息。这是全册追踪单风险点的合流现场,按嫌疑度排查:第一嫌疑 auto_ack(3.1 节,消费者崩溃即蒸发),第二嫌疑缺 delivery_mode=2(3.2 节,重启即蒸发),第三嫌疑路由静默丢弃(2.6 节,发出去就没进过队列),第四嫌疑 TTL 过期无死信(3.3 节,活太老被清)。每个嫌疑一条取证命令:
# 嫌疑一取证:未签收数是否异常归零(掉线即蒸发窗口) rabbitmqctl list_queues name messages_ready messages_unacknowledged # 嫌疑二取证:persistent 列与消息数是否一致 rabbitmqctl list_queues name messages persistent # 嫌疑三取证:绑定关系是否真的存在 rabbitmqctl list_bindings source_name destination_name routing_key | grep trace # 嫌疑四取证:队列是否声明了 TTL 而未配死信 rabbitmqctl list_queues name arguments
结果与解读:四条命令跑完,四类丢失原因基本现形。预防措施就是把 3.5 节的检查点地图前置到上线评审——急救手册治标,检查点治本。
故障三,重复消费型。现象:下游账务出现重复记录。根因必然是组合:消息队列的至少一次语义(重投递无法避免)叠加消费端无幂等。处置分两层:消费端加幂等表(消息 ID 或业务键唯一索引,重复插入直接签收跳过),业务流程加自然幂等设计(按主键更新而非插入)。认知纠偏:重复不是要"消灭"的故障,而是要"容忍"的特性——追求不重不丢的代价是分布式事务,绝大多数业务付不起,幂等才是标准答案(3.4 节判级与 5.5 节实现呼应)。
背景:演练环境注入复合故障——某服务发布代码更新后,队列开始堆积且偶发 406 报错。假装我们不知道原因,走一遍标准处置。
操作:第一步,三数分诊——发布速率正常、堆积增长、消费速率偏低,故障定在消费侧叠加配置异常。第二步,翻日志发现 406 PRECONDITION_FAILED:
rabbitmqctl list_queues name arguments # 预期输出: # trace.orders [] # trace.orders.retry [{"x-message-ttl", 60000}]
结果与解读:新版本代码把重试队列的 TTL 从六万改成三万毫秒,旧队列参数不可变——406 导致通道反复重连,消费吞吐随之下降。两个故障一个根因,正是 2.4 节"声明收敛"纪律要防的事故形态。处置:回滚该服务的队列声明到共享基础设施代码,重新部署,堆积在一小时内消化。复盘沉淀:把"队列声明必须走共享模块"写进代码评审 checklist——这是 6.1 节总表第 5 条的来历。
变式:演练只留 406 不留慢消费,观察故障表现如何"伪装"成纯堆积——单一根因在不同环境下表现多样,这正是"先定帧再定因"总纲的价值:帧序不受表象干扰。
手册之外,三个流程习惯决定深夜的混乱程度。习惯一,告警带上下文:告警消息里附上队列名、三数快照与最近变更,值班者不用从零猜。习惯二,处置动作留痕:每个操作记时间与结果,故障复盘与交接都靠它。习惯三,疑难故障升级有门槛:自己排查超过三十分钟无头绪就拉人——消息系统的故障窗口每多一分钟,堆积就多一分,单打独斗不是美德。
💡 关键直觉:故障排查的效率取决于"假设空间收窄的速度"。三数分诊一上来就砍掉三分之二的假设,帧序又砍一半——先分诊再深挖,永远比逐条试错快。
手册收尾。最后一节抬起头看路:版本怎么选、升级怎么做、消息队列的大方向在哪里。