先看现象。RabbitMQ 消费者 prefetch 设成 250,一条消息处理失败触发 nack 后整批重新入队,其他消费者又抢到同一批,日志里同一条任务出现 6 次。
把日志和配置对完,能解释这个现象的基本就这几处。预取数量过大,失败时影响面被放大。还有一种是nack 没有设置 requeue=false,直接重回队列头。
不容易想到的是,失败消息没有进死信队列,反复循环。
还有一种情况,多个消费者共享一个队列,重投后立刻被抢。
第一件事是看预取数量与失败率
python -c "print({'prefetch':250,'fail_rate':0.03})"
然后把预取降到个位数并开启手动确认
python -c "print({'prefetch':5,'ack':'manual'})"
失败消息转进死信队列单独处理
rabbitmqadmin declare queue name=task.dlq durable=true
剩下的就是重放死信队列观察是否还有重复
python replay_dlq.py --queue task.dlq --limit 100
怎么算真的好了,看的是同一批现象。同一条失败任务只出现一次,死信队列里能按错误类型分组,正常消息的处理延迟没有明显变化。
这里还有个坑。预取数量的本质是批量承诺,失败时这一批都要退回;宁可预取小一点、确认手动一点,也别让一条坏消息拖累两百条。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。
