百万级内容任务如何调度

内容生产系统最怕两件事:任务丢失和状态不可追踪。规模小的时候,人工重跑还能兜底;一旦进入百万级内容任务,必须把生成、下发、执行、回收、存储拆成清晰阶段,并且让每一步都能观察、重试和审计。

先定义任务,而不是先写队列

任务不是一行待处理文本,它应该包含业务标识、输入参数、目标站点、当前状态、重试次数、最近错误、创建时间和更新时间。只有任务模型稳定,后面的队列、缓存和执行器才不会变成一堆临时脚本。

状态流转要少而明确

推荐状态保持克制:pending 表示待下发,running 表示处理中,succeeded 表示已完成,failed 表示失败待处理,retrying 表示等待重试。状态太多会增加维护成本,状态太少又无法定位问题。

队列负责节奏,数据库负责事实

Redis、RabbitMQ 或其他队列适合承载执行节奏,但不要把队列当成最终事实来源。最终状态应该落库,队列只负责把任务送到执行器。这样即使队列重启,也能从数据库里恢复未完成任务。

队列消息要尽量轻

队列里放任务 ID 就够了,执行器拿到 ID 后再查完整任务。这样可以避免消息体过大,也能保证任务更新后执行器读到的是最新版本。

幂等:重试系统的地基

百万级任务一定会遇到超时、接口限流、网络抖动、进程重启。只要允许重试,就必须保证幂等。幂等不是“不重复执行”,而是重复执行不会造成错误结果。

常用幂等策略

对内容任务来说,可以用业务唯一键约束输出记录;保存结果前检查任务版本;外部接口调用记录请求指纹;写入存储时使用 upsert 或先查再更新。核心目标是让“同一个任务执行两次”不会产生两份互相冲突的内容。

失败处理:区别可重试和不可重试

并不是所有失败都应该重试。网络超时、临时限流、上游 5xx 可以进入重试;参数错误、目标不存在、内容校验不通过则应该直接失败并记录原因。盲目重试会浪费资源,也会掩盖真正的数据问题。

重试要有上限和退避

建议按错误类型设置最大次数,并使用递增间隔。比如第一次 1 分钟后重试,第二次 5 分钟,第三次 30 分钟。超过上限后进入人工处理列表,而不是无限循环。

可观测:让问题能被快速定位

可观测不是只看日志。至少要有任务总数、待处理数、运行中数量、失败数量、平均耗时、重试次数、上游错误分布。后台可以展示简单指标,日志里要带任务 ID,方便从一条失败追到完整链路。

日志要围绕任务 ID

每次状态变化都记录:任务 ID、旧状态、新状态、执行器、耗时、错误摘要。这样排查时不用猜,也能快速判断问题出在生成、回收、存储还是发布环节。

容量控制:不要让执行器把系统拖垮

并发不是越高越好。执行器应该按上游能力、数据库写入能力和机器资源设定并发上限。对外部模型或第三方接口尤其要做限流,否则短时间内大量失败会导致重试风暴。

落地清单

  • 任务表记录状态、重试次数、错误原因和更新时间。
  • 队列只放任务 ID,事实状态以数据库为准。
  • 每个写入动作都考虑幂等。
  • 失败分可重试和不可重试,重试有上限和退避。
  • 指标覆盖任务数量、失败率、耗时和错误类型。
  • 日志必须包含任务 ID,方便链路追踪。

内容调度系统的质量,不是看它高峰时能跑多快,而是看它失败时能不能恢复、问题能不能定位、结果能不能解释。规模越大,越要把状态、幂等和可观测放在第一位。