结合 Bull 的消息队列
在实际开发中,很多任务(如邮件发送、图片处理、数据导出等)不适合在主请求流程中同步执行。消息队列可以将这些耗时操作异步化,提升系统响应速度和可扩展性。NestJS 推荐使用 Bull 作为队列解决方案。
消息队列架构图示意
下图展示了在 NestJS 项目中,Bull + Redis 消息队列的典型架构与数据流转流程:
流程说明:
- 用户请求到达 Controller,业务 Service 通过 Producer 将任务添加到队列(Redis)。
- Redis 作为消息队列存储所有待处理任务。
- Consumer(Processor)从队列中取出任务,进行异步处理(如发送邮件、生成报表等)。
- 处理结果可通过回调、通知等方式反馈。
Bull 简介
Bull 是基于 Redis 的高性能消息队列,支持任务重试、延迟、优先级、并发等特性,适合 Node.js 场景下的异步任务处理。
- 支持任务持久化、失败重试、定时任务、任务优先级等
- 依赖 Redis,易于部署和扩展
- 与 NestJS 有官方集成包 @nestjs/bull
安装与环境准备
- 安装依赖:
npm install --save @nestjs/bull bull ioredis
- 本地需有 Redis 服务(可用 Docker 快速启动):
docker run -p 6379:6379 redis
在 NestJS 中集成 Bull
在主模块中引入 BullModule 并配置 Redis 连接:
// app.module.ts
import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';
import { MailModule } from './mail/mail.module';
@Module({
imports: [
BullModule.forRoot({
redis: {
host: 'localhost',
port: 6379,
},
}),
MailModule,
],
})
export class AppModule {}
创建队列与任务生产者
// mail/mail.module.ts
import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';
import { MailService } from './mail.service';
import { MailProcessor } from './mail.processor';
@Module({
imports: [
BullModule.registerQueue({ name: 'mail' }),
],
providers: [MailService, MailProcessor],
exports: [MailService],
})
export class MailModule {}
// mail/mail.service.ts
import { Injectable } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
@Injectable()
export class MailService {
constructor(@InjectQueue('mail') private mailQueue: Queue) {}
async sendMail(to: string, subject: string, content: string) {
// 添加任务到队列
await this.mailQueue.add('send', { to, subject, content });
}
}
创建任务消费者(Processor)
// mail/mail.processor.ts
import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
@Processor('mail')
export class MailProcessor {
@Process('send')
async handleSendMail(job: Job) {
const { to, subject, content } = job.data;
// 实际发送邮件逻辑(可调用第三方服务)
console.log(`发送邮件到: ${to}, 主题: ${subject}`);
// ...
}
}
典型实战场景:异步邮件发送
在用户注册、下单等场景下,调用 MailService.sendMail 即可异步发送邮件,无需等待任务完成即可响应用户。
// user.service.ts
import { Injectable } from '@nestjs/common';
import { MailService } from '../mail/mail.service';
@Injectable()
export class UserService {
constructor(private readonly mailService: MailService) {}
async register(username: string, email: string) {
// ...注册逻辑
await this.mailService.sendMail(email, '欢迎注册', '感谢您的加入!');
return { message: '注册成功' };
}
}
进阶用法与最佳实践
- 任务重试与失败处理:可在
add方法中设置重试次数、延迟等参数。 - 定时/延迟任务:如定时发送通知、延迟执行任务。
- 任务优先级与并发:支持设置任务优先级和并发消费数。
- 队列监控:可集成 bull-board 等可视化工具监控任务状态。
- 错误处理:在 Processor 中捕获异常,避免任务丢失。
- 合理拆分队列:不同业务建议用不同队列,便于扩展和监控。
1. 延迟任务与定时任务
// 延迟5分钟后发送通知
await this.mailQueue.add('send', { to, subject, content }, { delay: 5 * 60 * 1000 });
// 定时任务:每天8点发送(需结合 cron 表达式)
await this.mailQueue.add('send', { to, subject, content }, { repeat: { cron: '0 8 * * *' } });
2. 任务重试
// 失败后最多重试3次,每次间隔10秒
await this.mailQueue.add('send', { to, subject, content }, { attempts: 3, backoff: 10000 });
3. 集成 bull-board 可视化监控
bull-board 是 Bull 官方推荐的 Web UI,可实时查看队列、任务状态、失败原因等。
- 安装依赖:
npm install --save bull-board
- 在 main.ts 或专用模块中集成:
// main.ts
import { createBullBoard } from 'bull-board';
import { BullAdapter } from 'bull-board/bullAdapter';
import { Queue } from 'bull';
import { getQueueToken } from '@nestjs/bull';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const mailQueue = app.get<Queue>(getQueueToken('mail'));
const { router } = createBullBoard([
new BullAdapter(mailQueue),
]);
app.use('/admin/queues', router);
await app.listen(3000);
}
- 启动后访问
http://localhost:3000/admin/queues即可查看队列状态。
常见坑点
- Redis 未启动或连接异常,队列功能无法使用。
- Processor 未注册或命名不一致,任务无法被消费。
- 任务数据过大或频繁,需关注 Redis 性能瓶颈。
- 未处理任务失败和重试,可能导致消息丢失。
Bull 让异步任务处理变得简单高效,建议多查阅 Bull 官方文档 和 @nestjs/bull 文档 掌握更多高级用法。