Back

任务队列详解:工作进程、重试与调度

任务队列详解:工作进程、重试与调度

你的 API 端点需要在批量注册后发送 500 封欢迎邮件。你会让用户等待 45 秒,同步发送每封邮件吗?还是服务器会先超时?

这正是任务队列要解决的问题。它们让你将耗时的工作转移到后台进程,保持应用程序的响应性,同时任务在后台可靠地完成。

本文从概念层面解释任务队列的工作原理——重点介绍工作进程、重试策略和调度——帮助你自信地集成它们,而不会迷失在后端内部实现中。

核心要点

  • 任务队列存储需要后台处理的任务,保持应用程序的响应性,同时工作进程独立处理耗时操作。
  • 大多数队列系统提供至少一次交付,这意味着你的任务处理器必须是幂等的,以安全处理潜在的重复执行。
  • 为重试实现带抖动的指数退避,设置最大重试次数限制,并将永久失败的任务路由到死信队列以供审查。
  • 区分一次性延迟任务和周期性 cron 风格的调度,并注意重复执行和时区不匹配等陷阱。

什么是任务队列?

任务队列是一个存储任务以供稍后处理的系统。你的应用程序不是在请求中立即执行工作,而是将任务添加到队列中。后台工作进程随后检索这些任务并独立执行它们。

可以把它想象成餐厅厨房。服务员(你的 API)接受订单并将其传递给厨房(队列)。厨师(工作进程)按照自己的节奏准备菜肴。顾客不需要站在炉子旁等待。

常见用例包括:

  • 发送交易邮件
  • 处理上传的文件或图片
  • 生成报告
  • 与外部服务同步数据
  • 运行定期维护任务

后台任务工作进程如何运作

工作进程是专用进程,从队列中检索任务,执行它们,然后将其标记为完成或失败。

至少一次交付

这里有一个关键现实:大多数任务队列系统提供至少一次交付,而不是恰好一次。这意味着一个任务可能运行多次——如果工作进程在执行中途崩溃,另一个工作进程可能会重试相同的任务。

这不是 bug。这是为了可靠性而做出的刻意权衡。但这意味着你的任务处理器必须是幂等的:运行两次应该产生与运行一次相同的结果。如果你的任务是收取信用卡费用,你需要防止重复扣费的保护措施。

并发与扩展

工作进程通常并行运行。你可以通过添加更多工作进程来水平扩展。大多数现代队列系统允许你配置并发限制——单个工作进程同时处理多少任务。

优雅关闭是标准做法。在部署新代码时,工作进程应该完成当前任务后再停止,而不是中途放弃工作。

任务队列中的重试策略

任务会失败。网络会超时。外部 API 会返回错误。一个健壮的重试策略决定了你的系统是优雅恢复还是陷入混乱。

带抖动的指数退避

标准方法是指数退避:第一次重试前等待 1 秒,然后 2 秒,然后 4 秒,然后 8 秒。这可以防止持续攻击失败的服务。

添加抖动——小的随机延迟——可以防止惊群效应,即数百个失败的任务在完全相同的时刻重试。

重试限制和死信处理

设置最大重试次数。一个失败 10 次的任务可能有 bug,而不是暂时性错误。在耗尽重试次数后,任务通常会被移到死信队列或标记为永久失败以供人工审查。

区分可重试错误(网络超时、速率限制)和永久失败(无效数据、缺失资源)。不要在永远不会成功的任务上浪费重试。

延迟任务与调度

任务队列处理的不仅仅是即时任务。调度功能让你控制任务何时执行。

一次性延迟任务

安排任务在特定的未来时间运行:“24 小时后发送此提醒邮件。” 任务保持在队列中,直到其预定时间到达。

重复和 Cron 风格的任务

对于周期性工作——每日报告、每小时清理——大多数系统支持类似 cron 的调度。定义一个模式,系统会自动创建任务。

常见调度陷阱

重复执行:如果调度逻辑在多个地方运行,同一个周期性任务可能被创建多次。确保只有一个调度器实例负责创建预定任务。

停机间隙:如果你的系统在预定任务应该运行时停机,它可能被跳过或延迟执行。队列行为各不相同,应该提前了解。

时区:预定在 “上午 9 点” 的任务需要一个时区。UTC 对后端系统最安全,在面向用户的功能边缘进行转换。

前端任务队列集成

当用户发起的后台工作需要反馈时,前端应用程序通常通过状态轮询或实时更新与任务队列交互。

常见模式包括轮询端点获取任务状态、接收 WebSocket 更新或使用服务器发送事件(Server-Sent Events)。一种常见方法是立即返回任务 ID,然后让前端单独跟踪进度。

结论

任务队列将慢速工作移出请求路径,但它们需要有意识的设计。假设至少一次交付并构建幂等处理器。实现带抖动的指数退避和合理的重试限制。理解延迟一次性任务和周期性调度之间的区别。

这些不是高级附加功能——它们是生产系统的基本要求。掌握这些基础知识,你就能构建在负载下保持响应性同时可靠处理后台工作的应用程序。

常见问题

消息队列是用于在服务之间传递消息的通用系统,而任务队列专门管理后台任务,具有重试、调度和工作进程管理等功能。任务队列通常构建在消息队列之上,但添加了特定于任务的功能,如进度跟踪和死信处理。

使用唯一标识符跟踪已完成的工作,并在处理前检查。在数据库中存储幂等性密钥,使用带唯一约束的数据库事务,或利用外部服务功能,如 Stripe 的幂等性密钥。设计操作使重复执行除了第一次执行外没有额外影响。

当任务耗时超过几百毫秒、依赖可能失败的外部服务、需要在预定时间运行或可以从并行处理中受益时,使用任务队列。对于需要即时反馈的快速操作,保持同步处理。

从每个 CPU 核心一个工作进程开始,根据工作负载进行调整。IO 密集型任务(如 API 调用)可以处理每个工作进程更高的并发,而 CPU 密集型任务需要更多工作进程和更低的并发。监控队列深度和处理时间,为你的系统找到合适的平衡。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay