Web API 的 response time 應該在 100ms 以內,但發 Email、推播通知、呼叫外部 AI 服務可能要幾秒甚至幾十秒。解法很簡單:不要在 request/response 週期裡做這些事,丟進背景佇列,讓 worker 非同步處理。
BullMQ 是 Node.js 生態裡做這件事的最佳選擇。它底層用 Redis,成熟、有完整文件、TypeScript 原生支援。
BullMQ 是什麼
BullMQ 是 Redis-based 的 job queue library,前身是 Bull(BullMQ 是重寫版,API 更清楚,TypeScript first)。
核心概念三個:
- Queue:任務佇列,就是個有名字的 Redis key
- Job:丟進佇列的任務,帶有 payload 和配置(優先級、重試次數、延遲時間)
- Worker:消費佇列的 process,從 Redis 取 job 出來執行
Producer → Queue (Redis) → Worker
Redis 的 sorted set 和 list 確保 job 的原子性:同一個 job 不會被兩個 worker 同時取走,即使你開了多個 worker process。
安裝
npm install bullmq ioredis
基本用法
建立 Queue 和 Producer
import { Queue } from 'bullmq';
import Redis from 'ioredis';
const connection = new Redis({ host: 'localhost', port: 6379 });
const notificationQueue = new Queue('notifications', { connection });
// 丟 job 進去
await notificationQueue.add('send-email', {
userId: 'user-123',
type: 'mention',
mentionedBy: 'user-456',
});
建立 Worker
import { Worker } from 'bullmq';
const worker = new Worker(
'notifications',
async (job) => {
const { userId, type, mentionedBy } = job.data;
if (type === 'mention') {
await sendMentionEmail(userId, mentionedBy);
}
},
{ connection }
);
worker.on('completed', (job) => {
console.log(`Job ${job.id} completed`);
});
worker.on('failed', (job, err) => {
console.error(`Job ${job?.id} failed:`, err.message);
});
優先級、重試、延遲
優先級:數字越小優先級越高(1 > 10)
// P1:高優先,立即處理
await notificationQueue.add('in-app-p1', payload, {
priority: 1,
});
// P2:低優先,可以等
await notificationQueue.add('in-app-p2', payload, {
priority: 10,
});
自動重試:失敗了自動再試,指數退避避免轟炸外部服務
await notificationQueue.add('send-email', payload, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000, // 2s → 4s → 8s
},
});
延遲任務:N 毫秒後才讓 worker 取走
// 5 分鐘後才執行
await notificationQueue.add('digest-email', payload, {
delay: 5 * 60 * 1000,
});
排程(Cron):定時執行,就像 crontab
// 每 4 小時跑一次 Email 批次發送
await notificationQueue.add(
'email-batch',
{},
{
repeat: { pattern: '0 */4 * * *' },
}
);
島島的通知系統
島島的通知系統完全建在 BullMQ 上,有三條 worker pipeline:
使用者互動(按讚、留言、追蹤、mention)
│
▼
Notification Service(判斷 P1 / P2)
│
├── In-App Worker(立即建立通知記錄 → PostgreSQL)
│ ├── P1:個別通知(mention、夥伴申請)
│ └── P2:彙整通知(按讚、追蹤)
│
├── Email Worker(每 4 小時批次)
│ └── 撈出待發通知 → 合併同類 → 發送
│
└── Weekly Worker(每週排程)
└── 週報摘要 Email(實踐統計 + 互動彙整)
P1 vs P2 的設計邏輯
不是每個互動都需要立即通知。被 mention 很重要,馬上要知道;有人按讚你的貼文,等一下再告訴你也沒差。
P1(priority: 1):mention、夥伴申請、夥伴打卡——建立個別通知記錄,Email 批次時不彙整,直接發 P2(priority: 10):按讚、追蹤、留言——建立彙整通知(同類合併),Email 批次時合併成「3 個人按讚了你的貼文」
Email 批次 worker 的邏輯
// 每 4 小時跑一次
const emailBatchWorker = new Worker('email-batch', async () => {
const pendingUsers = await db.getUsersWithPendingNotifications();
for (const userId of pendingUsers) {
const notifications = await db.getPendingNotifications(userId);
const p1 = notifications.filter((n) => n.priority === 'P1');
const p2 = groupByType(notifications.filter((n) => n.priority === 'P2'));
await sendDigestEmail(userId, { p1, p2 });
await db.markNotificationsAsSent(userId);
}
});
監控
BullMQ 有官方的 dashboard:Bull Board,可以看到每個 queue 的 waiting / active / completed / failed job 數量,也可以手動重試失敗的 job。
npm install @bull-board/express @bull-board/api
import { createBullBoard } from '@bull-board/api';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { ExpressAdapter } from '@bull-board/express';
const serverAdapter = new ExpressAdapter();
createBullBoard({
queues: [new BullMQAdapter(notificationQueue)],
serverAdapter,
});
app.use('/admin/queues', serverAdapter.getRouter());
取捨
優點
- TypeScript first,型別完整
- 功能齊全:優先級、重試、延遲、Cron、批次
- Redis 保證 job 不會丟失(有持久化的話)
- Bull Board 監控介面直覺好用
- 可水平擴展:多個 worker process 同時跑,Redis 負責協調
缺點
- 依賴 Redis,Redis 掛了整個佇列就停了
- 不適合跨語言場景(Python worker 不能直接消費 BullMQ 的 job),島島的 Python AI 服務用 Celery 另外開
- Job payload 存在 Redis,不適合存大量資料(存 ID,讓 worker 自己去資料庫撈)
BullMQ vs 其他選項
| BullMQ | pg-boss | Celery | |
|---|---|---|---|
| 後端 | Redis | PostgreSQL | Redis / RabbitMQ |
| 語言 | Node.js | Node.js | Python |
| 優先級 | 原生支援 | 支援 | 支援 |
| TypeScript | 原生 | 支援 | 不適用 |
如果你已經有 PostgreSQL、不想加 Redis,pg-boss 是不錯的選擇。如果是 Python 後端,用 Celery。Node.js + Redis,BullMQ 沒什麼好猶豫的。
參考資料
- BullMQ 官方文件
- Bull Board 監控 Dashboard
- ioredis(Node.js Redis client)
- 島島阿學技術架構全覽 — BullMQ 通知系統 P1/P2 分級設計、Email 批次發送與週報排程的完整脈絡