问题现象:同一消息导致两次发货、两封邮件或两次ERP调用,不一定是RabbitMQ故障。消费者完成业务后若在ack前断开,Broker会重新投递。
| 阶段 | 实际操作 |
|---|---|
| 复现 | 在Publisher生成稳定message_id或业务event_id,Consumer日志只记录该ID、redelivered标志和处理结果,不记录完整敏感payload。 |
| 取证 | 在业务数据库建立event_id唯一记录,并在同一事务中检查/占用。已完成事件再次到达时直接返回成功;处理中超时需要有可恢复状态。 |
| 修复 | 外部API也应传幂等键。仅在本地去重但第一次外部调用成功、数据库写入失败,重试仍会重复调用外部系统。 |
| 回归 | 确认Consumer异常时正确抛出,让消息重试或进入失败处理;不要catch后既未完成业务又返回成功。设置最大重试与死信队列。 |
可直接执行的检查
bin/magento queue:consumers:list
bin/magento queue:consumers:start vendor.consumer --max-messages=100
# 业务表建议:UNIQUE(event_id, handler_name),并记录 processing/completed/failed 状态通过标准:在业务提交后、ack前主动终止Consumer,再启动处理;消息可以重投,但订单/发货/外部调用只产生一次最终副作用。
不要用消息正文哈希替代业务ID,字段顺序变化和合法重复事件会让哈希去重失真。

