支付回调里投的异步任务,日志显示同一个 order_id 扣款两次,间隔 47 秒,两次的 task_id 不一样。broker 的 redelivered 标记第一次是 False,第二次是 True。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

redis-cli SET job:pay:20261113001 1 NX EX 86400
celery -A app inspect active
# 返回 1 表示占位成功 0 表示重复投递

worker 执行到一半时连接被 broker 断开,消息回到队列又被另一个 worker 取走,重投的那份没有任何业务去重。任务参数里带着订单号,代码却只按 task_id 记账,两次投递就是两个 id。这一点先确认清楚,后面就不用反复试。

「Celery 任务重复扣款」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

具体做法是,把业务唯一键(订单号加操作类型)写成 Redis SETNX 的 key 进入任务先占位;占位成功才继续 失败直接返回上次的结果;过期时间要大于任务最长耗时 别在任务中途失效;补偿脚本按 order_id 反查流水 确认多扣的那笔能对上。

改完照着核一遍,手动触发两次同 order_id 的任务 日志里只出现一次扣款流水 第二次直接返回上次结果。

作者 9823189