LabHub
学习 学习路径 课程

队列与异步 API

做一套作业提交与状态轮询的 API

在 LabHub 中继续学习

目标

亲自实现以 202 Accepted 开始的异步 API 完整契约——接收、状态查询、轮询调节、幂等提交和完成通知。

为什么重要

如果把耗时 30 秒的任务做成同步 API,会同时破坏三件事:触发负载均衡器的空闲超时、客户端重试导致同一任务执行两次、工作进程每次被占用 30 秒。异步 API 将其拆分为接收和确认。然而,出乎意料的是,很多 API 只返回 202 就结束了。客户端不知道去哪里查看结果、不知道要等待多久,也不知道是否可以重试。本练习会逐一补齐契约中缺失的部分——Location 标头、状态词汇、Retry-After、幂等键以及 Webhook。全部具备之后,客户端才能放心地重试。

步骤

  1. 在 127.0.0.1:8140 启动 /root/aj/api.pyPOST /jobs 接收 {"n":5} 并将其放入队列 q:jobs2
  2. POST /jobs 响应状态为 202,包含 Location: /jobs/<job_id> 标头以及正文 {"job_id":"...","status":"queued"}
  3. GET /jobs/<job_id> 返回 200 和 status。其值为 queuedrunningsucceededfailed 之一。不存在的 id 返回 404。
  4. /root/aj/worker.py 从队列取出任务,将状态改为 running,处理后再改为 succeeded
  5. 已完成任务的查询响应中必须有 result 键。处于 queued 状态时不得有 result 键。
  6. queuedrunning 状态的响应应带有以整数秒表示的 Retry-After 标头,而 succeeded 响应不应包含它。
  7. 使用同一个 Idempotency-Key 提交两次时,应返回同一个 job_id,并且队列中只能有 1 条任务。
  8. 在 127.0.0.1:8141 启动 /opt/app/hooksink.py。提交时同时接收 callback_url,任务完成后向该地址发送 POST。/root/aj/hook.log 中应记录 job_idstatus=succeeded

参考

启动任务提交 API

在 127.0.0.1:8140 启动 /root/aj/api.pyPOST /jobs 接收 {"n":5} 并将其放入队列 q:jobs2

提交接口将任务放入队列后立即响应。不要实际处理任务——这正是异步的核心。

返回 202 和 Location 标头

POST /jobs 响应状态为 202,包含 Location: /jobs/<job_id> 标头以及正文 {"job_id":"...","status":"queued"}

只有同时说明“已接收”和“在哪里查看”,契约才成立。标头和正文缺一不可。

创建状态查询端点

GET /jobs/<job_id> 返回 200 和 status。其值为 queuedrunningsucceededfailed 之一。不存在的 id 返回 404。

四种状态词汇就足够了。对于不存在的 job id,请返回 404。

让工作进程推进状态

/root/aj/worker.py 从队列取出任务,将状态改为 running,处理后再改为 succeeded

从队列取出后状态为 running,结束后为 succeeded。将状态存入 Redis 哈希会比较方便。

完成后加入结果

已完成任务的查询响应中必须有 result 键。处于 queued 状态时不得有 result 键。

如果结果较小,可以随状态响应一起返回。完成之前不得出现 result 键。

建议轮询间隔

queuedrunning 状态的响应应带有以整数秒表示的 Retry-After 标头,而 succeeded 响应不应包含它。

只在任务进行中提供该标头。如果已完成响应中仍有这个标头,客户端会继续轮询。

使提交具有幂等性

使用同一个 Idempotency-Key 提交两次时,应返回同一个 job_id,并且队列中只能有 1 条任务。

如果在收到 202 之前连接中断,客户端会重试。对同一个键请返回同一个标识符。

发送完成 Webhook

在 127.0.0.1:8141 启动 /opt/app/hooksink.py。提交时同时接收 callback_url,任务完成后向该地址发送 POST。/root/aj/hook.log 中应记录 job_idstatus=succeeded

先启动接收器,并在提交时同时接收回调地址。必须留下接收记录才能通过评分。