cswork / 系统设计

第 39 章 / 共 57 章 · 约 4 分钟

设计模式:长时间运行的任务

本页目录
  1. 1. 基本流程
  2. 2. 可靠性要点
  3. 3. 选择队列
  4. 4. 扩缩容和公平性
  5. 5. 常见追问
  6. 6. 本站用到这个模式的案例
  7. 章节测验

视频转码要几分钟,生成一份年度报表要十几分钟,爬一个网站要几个小时。这类工作不能塞进一次 HTTP 请求里:请求会超时,用户会刷新重试,服务器的连接和线程被长时间占住。通用的做法是异步任务:接口立即接下任务、返回一个任务 id,由后台的 worker 慢慢做,结果通过查询或通知交给用户。

1. 基本流程

  1. 客户端提交任务,服务端写一条任务记录(状态 QUEUED),把任务放进队列,立即返回 202 Accepted 和任务 id。
  2. worker 从队列取任务,把状态改为 RUNNING,开始处理。
  3. 完成后写入结果(大结果放对象存储),状态改为 SUCCEEDED 或 FAILED。
  4. 客户端通过轮询 GET /jobs/{id}、webhook 回调或实时推送得知结果。
异步任务的基本流程
滚动到此处后加载图表…
图表可横向滚动,展开后可缩放查看。
查看图表源码
flowchart LR
  accTitle: 异步任务的基本流程
  c["客户端"] -->|"POST /jobs"| api["API 服务"]
  api --> db[("任务表:状态和结果位置")]
  api --> q["队列"]
  api -->|"202 + job_id"| c
  q --> w["Worker 池"]
  w --> db
  w --> s3[("对象存储:结果文件")]
  c -.->|"轮询 / webhook / 推送"| db

2. 可靠性要点

  • 可见性超时和心跳:worker 取走任务后,任务在队列里暂时不可见;worker 崩溃没确认,超时后任务重新出现,交给别的 worker。任务比超时时间长时,worker 要定期续期(心跳),否则同一个任务会被两个 worker 同时处理。
  • 幂等:重新投递一定会发生,任务处理要能安全地重复执行:结果按任务 id 覆盖写,外部副作用带幂等键。
  • 重试和死信:失败后按指数退避重试,次数用尽进入死信队列(DLQ),由人排查。区分可重试的错误(超时、限流)和不可重试的错误(输入格式错误)。
  • 检查点:几个小时的任务要分段保存进度,失败后从最近的检查点继续,而不是从头开始。
  • 拆分:一个大任务拆成很多小任务并行处理,再合并结果。视频按片段转码就是这样。

3. 选择队列

选项适合代价
SQS / RabbitMQ典型的任务队列:逐条确认、可见性超时、延迟、死信不能重放已经处理过的任务
Kafka任务量极大、需要按 key 有序或重放单条重试和延迟投递要自己做
Redis 列表 / Stream轻量、低延迟受内存限制,持久性弱一些
数据库表加 SKIP LOCKED量不大、想少一个组件吞吐受数据库限制
工作流引擎多步骤、长等待、需要定时多一个系统,见多步骤流程

4. 扩缩容和公平性

  • 按队列深度扩缩容:积压越多,worker 越多;积压为零时缩到最小。按 CPU 扩缩容对 I/O 密集的任务不准。
  • 优先级:付费用户、交互式任务放高优先级队列,批量任务放低优先级队列,worker 优先消费高优先级。
  • 多租户公平:一个大客户一次提交一万个任务,不能把其他客户饿死。可以按租户分队列轮询,或者限制每个租户同时运行的任务数。
  • 资源隔离:GPU 任务、内存大的任务用单独的 worker 池。

5. 常见追问

  • 用户提交后关掉页面,任务还会继续吗? 会,任务和请求解耦了;用户回来后按任务 id 查结果,或者收到邮件 / 推送。
  • 同一个任务被提交了两次怎么办? 客户端带幂等键提交,服务端发现重复就返回已有的任务 id。
  • 怎么取消正在运行的任务? 任务表里标记取消,worker 在检查点之间检查这个标记并主动停止。
  • 定时任务(每天凌晨执行)怎么做? 调度器只负责按时把任务放进队列,执行仍然交给 worker;调度器本身要单实例或选主,避免重复触发(见 ZooKeeper / etcd)。

6. 本站用到这个模式的案例

章节测验

5 道选择题
  1. 生成一份报表要 10 分钟。API 应该怎样设计?
  2. 队列的可见性超时是 5 分钟,但有的任务要跑 20 分钟,结果同一个任务经常被两个 worker 同时处理。怎么修?
  3. 一个任务因为输入文件格式错误而失败。重试策略应该怎么处理它?
  4. 一个大客户一次提交了 1 万个任务,其他客户的任务排在后面要等好几个小时。怎么改进?
  5. 视频转码 worker 的扩缩容应该依据什么指标?

章节讨论

正在加载讨论…