Ready消息只有几十条,Unacked却持续增长,重启consumer后短暂恢复。根因往往不是队列吞吐,而是consumer取到消息后调用外部ERP没有超时,prefetch额度被占满,消息既不ack也不回队列。
问题不在表面
先用一个真实失败样本复现,不要一上来全量重建。把请求、实体ID和时间记下来,后面的SQL与日志才能对得上。
几条命令足够定位
- RabbitMQ管理页同时看messages_ready、messages_unacknowledged和consumer utilisation。
- 把consumer PID对应的网络连接、PHP堆栈与外部接口响应时间对上。
rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers rabbitmqctl list_channels connection pid consumer_count messages_unacknowledged prefetch_count bin/magento queue:consumers:list timeout 5 curl -v https://erp.example.com/health
这里的重点不是把报错暂时消掉,而是让同一操作再执行一次仍保持幂等;因此修复后一定要做第二次重复执行测试。
改动落在哪里
给HTTP客户端配置连接超时和总超时,并对可重试错误做有限次数退避;不可重试的业务错误落失败表或死信队列。降低合理的prefetch,配合max-messages让进程定期退出释放内存。发布前先停止旧consumer,发布后由Supervisor逐个拉起,避免新旧代码并行消费。
实施时的顺序
- 先做数据与配置备份,记录当前值。
- 只改一个变量并重跑失败样本。
- 确认新数据正确后,再处理历史受影响记录。
- 最后恢复定时任务或缓存。
验收清单
- Unacked能在超时窗口内回落
- 断开ERP时消息按策略重试或进死信
- 恢复ERP后积压被平稳消费
- 同一业务单号保持幂等
验收不只看后台提示“成功”。我还会从前台/API再走一次真实链路,并在下一轮cron、索引或缓存命中后重复检查,确认不是短暂正常。
不要这样处理
直接purge只会丢Ready消息,既解决不了Unacked,也可能造成订单数据永久缺失。

