在 JavaScript (Node.js) 中处理任务队列
在现代 Web 应用和微服务架构中,经常需要执行不应阻塞主执行线程的后台任务。任务队列是一种强大的工具,有助于管理异步操作,确保可靠性、可扩展性和容错性。本文将详细介绍如何在 Node.js 环境中处理任务队列,探讨主要库并编写实用的代码示例。
什么是任务队列,为什么需要它?
任务队列 (Task Queue) 是一种按 FIFO(先进先出)原则工作的数据结构。任务被放入队列,并由工作进程 (worker) 按到达顺序处理。主要使用场景包括:
- 发送电子邮件通知 — 无需让用户等待邮件发送完成。
- 生成报告或 PDF — 资源密集型操作,最好异步执行。
- 处理上传的图片 — 调整大小、压缩、添加水印。
- 与外部 API 集成 — 执行带频率限制 (rate limiting) 的请求。
- 计划任务 (cron) — 定期清理数据库或更新缓存。
使用队列可以减轻主服务器负担,提高容错性(如果某个工作进程崩溃,任务将传递给另一个),并确保即使在临时故障时也能执行任务。
Node.js 中的简单内存队列
首先,我们实现一个没有外部依赖的最简单队列。这种方法适用于原型设计或非常小的项目,但由于缺乏持久性,不建议用于生产环境。
class SimpleQueue { constructor() { this.queue = []; this.processing = false; }
enqueue(task) { this.queue.push(task); this.processNext(); }
async processNext() { if (this.processing || this.queue.length === 0) return; this.processing = true; const task = this.queue.shift(); try { await task(); } catch (err) { console.error('任务执行错误:', err); } finally { this.processing = false; this.processNext(); } }}
// 使用示例const queue = new SimpleQueue();
queue.enqueue(async () => { console.log('任务 1: 发送电子邮件');});
queue.enqueue(async () => { console.log('任务 2: 生成报告');});
console.log('任务已添加到队列');这段代码演示了基本机制:任务按顺序逐个执行。但是,如果服务器重启,所有未处理的任务都将丢失。
Bull — 基于 Redis 的专业队列
对于 Node.js 中的严肃项目,事实上的标准是 Bull 库。它使用 Redis 作为后端,提供持久性、优先级支持、重试机制和延迟任务。
安装:
npm install bull简单的生产者和消费者示例:
const Queue = require('bull');
// 创建一个名为 'email-queue' 的队列const emailQueue = new Queue('email-queue', { redis: { host: 'localhost', port: 6379 }});
// 工作进程函数:处理任务emailQueue.process(async (job) => { const { to, subject, body } = job.data; console.log(`发送邮件至 ${to}: ${subject}`); // 这里应该是实际的邮件发送代码 await new Promise(resolve => setTimeout(resolve, 1000)); return { success: true };});
// 添加任