现在创建任务先写数据库,再调用 RabbitMQ。两步之间进程退出,数据库里会有任务,队列里却可能什么也没有。

要解决什么

让已经接受的任务最终有机会执行。这里假设数据库和消息系统的数据没有永久丢失,故障后能够恢复,不能承诺所有灾难下绝不丢失。

有哪些方案

可以让请求线程不断重试,可以定时扫描待处理任务,也可以在数据库里记下一张“待发送清单”。我选最后一种,也就是 outbox。任务变更和发送意图用同一个任务库事务提交。

第一版怎样做

03_taskService 增加 outbox 表:event_idstep_idgeneration、事件内容、available_at、发送次数、认领期限、发送时间。generation 表示这一步第几次被安排执行,用来识别过期消息。

创建步骤时,事务同时插入一条 outbox 记录;步骤推进和延迟重试也走这条路。事务里只写数据,不连 RabbitMQ,更不等待模型运行。文件签名等外部操作留到领取任务以后处理。

单独的发送循环分批认领到期记录,提交认领事务,再向 RabbitMQ 发送。发布端启用 confirm,并检查 mandatory 返回:消息被 broker 接受和消息确实路由到队列,都要考虑。成功后标记已发送;连接断开或结果不明,留待重试。RabbitMQ 确认机制

多发送器时,可用短事务里的 FOR UPDATE SKIP LOCKED 配合认领期限,避免它们同时占住同一批记录;别在持有行锁时等网络。PostgreSQL 16 锁定说明

发送成功、标记之前仍可能崩溃,所以消费者必须容忍重复。用步骤状态、generation 和下一篇的领取事务决定消息还能不能执行。消息 ID 去重本身不能代替业务状态判断。

现有 Distribute 调用点要逐步替换成登记 outbox;不要保留同步发送和 outbox 双重投递作为正常路径。恢复机制完成后,再清理旧路径。

怎么验收

准备三个断点:事务提交前、提交后发送前、confirm 后标记前。分别终止进程并重启。第一种没有残缺任务;第二种能补发;第三种可能重复送达,但只能形成一次有效的步骤完成。

最后停掉 RabbitMQ,创建任务应得到可查询的等待状态;恢复后自动发出。记录“队列中出现两次”与“业务执行结果提交两次”的差别。

本篇解决投递恢复。处理失败怎样重试,留到下一篇一起定。

目录 · 下一篇:执行尝试与租约