问题现象:同一消息导致两次发货、两封邮件或两次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,字段顺序变化和合法重复事件会让哈希去重失真。