Mac 休眠十分钟,再醒来上传结果,这个结果还算不算数?如果另一台机器已经接手,光有一个 task_id 就不够判断了。

要解决什么

允许任务重试,同时防止旧执行覆盖新结果。还要分开两类故障:进程消失、业务处理返回错误。当前 Worker 在 Handle 返回错误后仍 Ack,并没有完整的业务重试流程。

有哪些方案

只依靠队列重投,分不清旧结果;用永久锁,机器离线后容易一直卡住。我选有期限的执行租约,数据库保存当前 attempt,队列只负责通知。

第一版怎样实现

收到消息后,Worker 带节点身份、step_idgeneration 调任务服务领取。事务确认步骤可执行、generation 一致、没有有效租约,才创建 attempt 并返回租约。两个节点同时领取,只有一个成功。

执行期间按间隔续租,只允许当前 attempt 在尚未过期时续期,时间以数据库为准。可先试每 15 秒续租、有效期 60 秒;这些是实验参数,后面按实际网络延迟调整。进程心跳不代表任务一定健康,还要有步骤总超时。

产物写到 attempt 专属位置。完成时先验证产物,再在事务内检查 attempt、步骤状态和租约有效期,提交最终产物引用及后续 outbox。已经成功的相同 attempt 返回之前的结果;旧 attempt 返回明确的过期响应。即使物理上算了两次,也只接受一个有效结果。

租约过期扫描器通过条件更新回收步骤;增加 generation,按重试策略生成新的 outbox。它与完成请求并发时,也必须争用同一个状态条件,不能各自成功。

第一版仍让每个执行槽位持有消息到结果处理完毕:

情况处理方式
成功或永久失败已持久化Ack
可重试失败,重试计划已入库Ack,等待新的到期消息
消息已过期或任务已有有效执行Ack,不重复运行
无法确认或保存业务状态不 Ack;退避恢复,必要时断开让队列重投

错误分类先做网络错误、资源不足、无效媒体、已取消。暂时性故障有限重试,例如最多三次、指数退避加随机抖动;无效媒体直接结束。不要无限立即 requeue。RabbitMQ Ack 与重投语义

长任务还要核对 broker 的消费者确认超时,使其覆盖限定的步骤执行时间。不能只加心跳,就认为消息可以永远不确认。当前同步推理也没有完整的取消链路;需要能终止执行,至少做到过期结果被拒绝。

怎么验收

让 A 领取任务后断网,等 B 接手完成,再让 A 上报。最终结果只能来自 B。另测“完成已提交但响应丢失”:A 再次上报应得到相同成功结果,不能再生成下游任务。

03_taskService 的领取和完成事务开始,再改两个 Worker 的上报协议与 Ack 分支。一次演示这两个竞态,胜过只展示一个重试按钮。

目录 · 下一篇:异构节点分配