RabbitMQ:故障定位、恢复与交付练习
队列诊断与重启保留
本轮仅静态审阅,以下命令与练习均未执行;预期结果不是本轮运行证据。早期真实服务验证仅为历史记录,不能代替当前环境验证。
从症状到对象
连接拒绝:检查 25672 映射、进程、主机地址;认证拒绝:检查用户与 vhost 权限,别把宿主机当容器 loopback;PRECONDITION_FAILED:对比声明的 durable、队列类型与 arguments;UnroutableError:检查 exchange 绑定和 routing key。先识别层次再修改,不用删数据代替诊断。
docker exec showdocs-m4b-rabbitmq-broker rabbitmqctl list_connections name user state
docker exec showdocs-m4b-rabbitmq-broker rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers durable
docker exec showdocs-m4b-rabbitmq-broker rabbitmqctl list_bindings source_name destination_name routing_key
docker logs --tail 100 showdocs-m4b-rabbitmq-brokerready 增长说明领取速度不足或没有消费者;unacknowledged 长期不降说明消费者收到却未确认。ready=0 且 unacked>0 不能当作任务全部完成。消费者数量和处理时延要一起看;盲目扩容可能把下游数据库压垮。
持久消息的受控重启
先发布并收到确认,再重启,恢复就绪后领取原始内容。固定 hostname 保持节点名,数据写入 showdocs-m4b-rabbitmq-data,避免容器重建时因为节点名变化误以为没有数据。
python examples/rabbitmq/main.py --prefix recovery-jobs produce --value survives-restart
docker restart showdocs-m4b-rabbitmq-broker
docker exec showdocs-m4b-rabbitmq-broker rabbitmq-diagnostics -q ping
python examples/rabbitmq/readiness.py
python examples/rabbitmq/main.py --prefix recovery-jobs consume预期恢复后取得 survives-restart。需先按 项目装好依赖,并使用全新 recovery-jobs(重跑时换名),确保队列只有这一条消息。ping 只检查节点,AMQP readiness 也成功后再领取;任一探测失败应有界重试,约 80 秒仍失败就停止并查日志。该实验只证明命名卷下受控重启保留,不是电源故障、异机恢复、备份完整性或节点 HA 测试。volume 丢失仍会丢数据,单节点 durable 不能替代冗余和备份。
死信与补偿运维
读取死信时保存 message_id、原始 body、x-death 中的 queue/reason/count 和业务失败原因。修复格式或下游条件后,使用明确业务键重新发布,并让最终业务幂等;不要把全部死信无条件回灌原队列。死信反复循环会放大错误,应该有人工审阅或明确有限重试策略。
安全清理范围
docker compose -p showdocs-m4b-rabbitmq -f examples/rabbitmq/compose.yaml downdown 停止并删除本任务容器与网络,保留数据卷。额外添加 -v 会删除全部实验队列与消息,先明确数据不再需要。不要执行无范围的 docker system prune,原站点容器 showdocs-showdocs-1 与其他任务服务应保持不动。
本篇的机制与失败分支可按综合练习书面推演;提示与参考答案分开提供,无需执行示例。
练习与参考解法
本轮仅静态审阅,以下命令与练习均未执行;预期结果不是本轮运行证据。早期真实服务验证仅为历史记录,不能代替当前环境验证。
路由与确认练习
前置:完成 项目安装与连接。下面命令使用全新 exercise-jobs;重做时全部替换为新 prefix,避免旧消息影响领取。
python examples/rabbitmq/main.py --prefix exercise-jobs setup
python examples/rabbitmq/main.py --prefix exercise-jobs produce --value job-A --route created
python examples/rabbitmq/main.py --prefix exercise-jobs produce --value job-B --route missing
python examples/rabbitmq/main.py --prefix exercise-jobs consume --action requeue
python examples/rabbitmq/main.py --prefix exercise-jobs consume --action dead
python examples/rabbitmq/main.py --prefix exercise-jobs consume --dead --action ack
python examples/rabbitmq/main.py --prefix exercise-jobs produce --value job-C
python examples/rabbitmq/main.py --prefix exercise-jobs consume --action leave
python examples/rabbitmq/main.py --prefix exercise-jobs consume --action ack提示:missing 这一步预期退出 1,其余预期 0;不要把预期失败当作继续吞掉任何错误的理由。逐步记录 value、action 和 redelivered;只在死信消息上检查 x-death。leave 关闭后重新入队可能需要时间,本 CLI 用有界轮询等待。job-D 重启步骤可按 运维示例把 prefix 换成 exercise-jobs 完成,先确认此前队列已清空。
用新的 prefix 声明资源。发布 route=created 的 job-A,再发布 route=missing 的 job-B;记录两次退出码。领取 job-A 时先 requeue,再 dead,最后从死信队列 ack。每步记录 value、redelivered、x-death,说明哪一步改变了 broker 对消息的责任。
第二组练习发布 job-C,领取时选择 leave 后退出,再启动消费者 ack。第三组发布 job-D 后重启服务,等待 ping 成功再领取,解释 durable、delivery_mode 和 confirm 各自承担什么。
参考解法
完成练习后展开参考解法
参考答案:created 命中 direct binding,发布确认后成功;missing 无绑定,mandatory 让返回的不可路由消息触发 UnroutableError。发布确认不是消费者确认,因此 producer 成功不会删除 work 队列消息。
requeue=True 让 job-A 留在原队列再次可投递,下一次 redelivered=true;dead 对应 requeue=False,经 DLX 路由到 dead 队列,x-death 的 reason 为 rejected。死信消费者 ack 才完成该次死信投递。leave 后关闭连接也会使 job-C 重新投递,业务需要处理重复。
durable 保存资源定义,delivery_mode=2 请求消息持久化,confirm 让发布者知道 broker 接受了消息责任。本实验受控重启保留 job-D,但不能推导单节点抗磁盘故障或公网生产可用。
排错问题与答案
先自行解释:①ready=0 为什么不保证完成?②为什么不能无限 nack(requeue=True)?③发布结果不确定时重发,message_id 能自动去重吗?
提示:区分 ready 与 unacknowledged;考虑消息格式永久错误,以及 confirm 在返回途中丢失。
展开排错参考答案
ready=0 时消息可能在消费者手里仍未确认,应结合 unacked、日志与业务结果判断。永久毒消息反复 requeue 会热循环,应隔离并审阅。确认返回丢失后重发可能造成重复,broker 一般不按 message_id 自动去重,必须用明确持久化的业务去重规则。
学习范围
交付 AMQP 0-9-1、direct 路由、confirm/mandatory、manual ack/nack、死信、持久重启、有界 CLI 和诊断。没有把 basic_get 实验冒称持续消费者压测,没有验证 quorum 多节点容错、TLS、生产权限或跨地域灾备;这些需要独立配置和失效实验。
继续阅读
- 完整实践
- 知识专题
- 知识专题