模块 08第 3 学期 · 可靠性与扩展10 海报条目

异步、事件与实时

登录后可在不同设备间同步进度。在此之前,进度只保存在当前浏览器中。

为什么重要

发送邮件、生成 PDF、调用 LLM agent、处理 webhook:如果这些都在用户请求里同步执行,请求就会变慢,会撞上 serverless 的时长限制而超时,一旦崩溃还会丢失工作。靠谱的系统正是靠异步设计来保持快速、不丢数据。

学习目标

  1. 把耗时或不可靠的工作移到持久化的后台任务中。
  2. 在队列、pub/sub 和事件驱动设计之间做出选择。
  3. 安全地处理 webhook(验签、去重、快速确认、异步处理)。
  4. 用 saga 和 outbox 模式协调多步骤工作流。
  5. 选对实时传输方式:WebSockets、SSE 或长轮询。
  6. 让定时任务即使重叠执行或错过一次也安全。

课程内容

8.1 队列与 worker(15)

模式:请求做校验,把意图写入数据库,任务入队,返回带任务 ID 的 202 Accepted。worker 负责处理、更新状态、发送通知。

  • 实际上投递语义是至少一次(at-least-once):每个任务处理函数都要设计成幂等的。
  • 可选方案:托管服务(SQS、Cloud Tasks)、基于 Redis(BullMQ)、基于 Postgres(pg-boss、Graphile Worker)、持久化工作流引擎(Inngest、Trigger.dev、Temporal)。
  • 持久化工作流引擎往往最适合 AI agent:每一步都会做 checkpoint,并能独立重试。

8.2 Pub/Sub 与事件驱动架构(16, 17)

  • 队列: 一条消息,一个消费者(一项要做的任务)。
  • Pub/Sub: 一个事件,多个互相独立的订阅者(一件已经发生的事实:order.paid)。
  • 事件驱动: 服务对事件做出反应,而不是彼此直接调用。解耦了,但更难追踪;需要事件 schema 和良好的可观测性。
  • 事件用过去时命名;给事件 schema 做版本管理;带上事件 ID 以便去重。

8.3 分布式事务、outbox 与 saga(18, 19)

  • 你不可能让数据库、Stripe 和邮件服务商共处同一个 ACID 事务。
  • Transactional outbox(事务性发件箱): 在同一个数据库事务里,同时写入业务变更和一条 outbox 记录;由中继程序发布 outbox 记录。这样可以避免“保存了但没发布”或“发布了但没保存”。
  • Saga: 一连串本地步骤,每一步都有对应的补偿操作(锁定库存 → 扣款 → 创建发货单;如果发货失败:退款、释放库存)。可以是编排式(有一个协调者),也可以是协同式(通过事件)。
  • 两阶段提交确实存在,但在现代 Web 系统中很少适用。

8.4 Webhook(101)

接收方:

  1. 验证签名(用共享密钥做 HMAC)和时间戳,防止伪造和重放。
  2. 按服务商的事件 ID 去重(唯一约束)。
  3. 快速确认(几秒内返回 2xx),然后在后台任务里处理。
  4. 处理乱序投递;拿不准时向服务商拉取当前状态。 发送方:对 payload 签名,带退避重试,为客户提供投递日志和重放功能,防范客户提供的 URL 带来的 SSRF。

8.5 定时任务(21)

  • 假设任何一次运行都可能被跳过、延迟或执行两次。让任务幂等且可续跑。
  • 用锁防止重叠执行(数据库 advisory lock)。
  • 分批处理并做 checkpoint,而不是一个巨大的事务。
  • 用“dead man's switch”心跳来监控(任务没有运行时告警)。
  • 调度时间用 UTC 存储;注意其他市场的夏令时。

8.6 实时传输(22, 23, 24)

传输方式 方向 适用场景
SSE 服务端到客户端 流式输出 LLM token、通知、进度。简单,基于 HTTP,自动重连
WebSockets 双向 聊天、协作、实时游戏、语音
长轮询 模拟推送 流式传输被拦截时的兜底方案
注意事项:连接要认证,长连接要定期重新授权,处理重连和漏掉的消息(从最后一个事件 ID 续传),serverless 平台可能限制连接时长;规模上来后使用托管实时服务(Supabase Realtime、Pusher、Ably、Cloudflare Durable Objects)。

实验 8:改成异步

  1. 把你最慢的同步操作(LLM 调用、PDF、邮件)移到持久化任务里,通过状态轮询或 SSE 展示进度。
  2. 实现一个 webhook 接收端(Stripe 或 Resend),包含验签、去重和异步处理。把同一个事件重放 10 次;证明只产生一次效果。
  3. 实现一个带补偿的 3 步 saga;在第 3 步注入失败;证明能正确回滚。
  4. 加一个带防重叠锁和心跳告警的定时任务。

完成标准(Definition of Done)

AI 提示词包

  • “找出请求处理函数中所有调用外部服务或可能超过 1 秒的操作。建议哪些应该移到后台任务。”
  • “实现这个 webhook 处理函数:用恒定时间比较验证签名,拒绝过期的时间戳,用唯一约束按 event id 去重,把处理任务入队,返回 200。”