引言:为什么你的服务器在应该工作时却“休眠”
\n\n想象一下:用户点击“发送报告”按钮,页面卡顿 30 秒。服务器同步生成 PDF、压缩图片并发送电子邮件——所有操作都是同步的。熟悉吗?如果是,那么你就陷入了阻塞代码的陷阱。
\n\n在本文中,我们将探讨 TypeScript 中的队列和后台任务 如何解决这个问题。您将学习如何将繁重的工作转移到后台进程,同时保持对错误和可扩展性的控制。本文面向已经熟悉 Node.js 并希望将项目提升到新水平的开发者。
\n\n我们将讨论 Bull 和 BullMQ 库,涉及 RabbitMQ,并展示 TypeScript 中的真实代码示例。开始吧!
什么是任务队列以及为什么需要它
\n\n任务队列是一种将用户请求与其执行分离的机制。我们不是直接在 HTTP 处理器中处理操作,而是将任务发送到队列,后台工作进程会获取并异步执行它。
\n\n典型使用场景
\n- \n
- 发送电子邮件和推送通知 \n
- 生成 PDF、图像、视频 \n
- 数据解析和网页抓取 \n
- 在线商店订单处理 \n
- 批量计算和报告 \n
没有队列,您的应用程序可能会在高负载下崩溃。有了队列,您将获得 容错性(如果工作进程崩溃,任务不会丢失)和 可扩展性(可以运行 10 个工作进程而不是一个)。
\n\nBull 和 BullMQ:Node.js 的强大队列
\n\nBull 是 Node.js 生态系统中用于处理队列的最流行库。它基于 Redis,并原生支持 TypeScript。BullMQ 是其继任者,更现代,支持沙箱(隔离进程)和高级管理。
\n\n安装和基本配置
\nnpm install bullmq ioredis\nnpm install -D @types/bull\n\n用 TypeScript 创建一个队列和工作进程:
\n\n// queue.ts\nimport { Queue } from 'bullmq';\n\nconst connection = {\n host: 'localhost',\n port: 6379,\n};\n\nexport const emailQueue = new Queue('email', { connection });\n\n// worker.ts\nimport { Worker } from 'bullmq';\nimport { sendEmail } from './emailService';\n\nconst worker = new Worker('email', async job => {\n const { to, subject, body } = job.data;\n await sendEmail(to, subject, body);\n console.log(`Email sent to ${to}`);\n}, { connection });\n\nworker.on('completed', job => {\n console.log(`Job ${job.id} completed`);\n});\n\nworker.on('failed', (job, err) => {\n console.error(`Job ${job.id} failed: ${err.message}`);\n});\n\n现在,我们可以在应用程序的任何位置添加任务:
\n\nimport { emailQueue } from './queue';\n\nawait emailQueue.add('send-welcome', {\n to: 'user@example.com',\n subject: '欢迎!',\n body: '感谢您的注册...'\n});\n\n很简单,对吧?但这只是开始。BullMQ 提供了更多功能。
\n\nBullMQ 的高级功能
\n- \n
- 重试 — 如果任务失败,可以在 N 秒后自动重新启动。 \n
- 延迟执行 — 将任务推迟到特定时间。 \n
- 优先级 — 紧急任务优先处理。 \n
- 事件 — 可以订阅进度、完成、错误事件。 \n
- 沙箱 — 在隔离进程中执行代码(安全 + 干净)。 \n
带有重试和延迟的示例:
\n\nawait emailQueue.add('send-invoice', {\n invoiceId: 123,\n email: 'client@example.com'\n}, {\n attempts: 5,\n backoff: { type: 'expon