“Cronjob là trái tim của nhiều hệ thống backend – nhưng nếu không thiết kế phân tán, nó có thể trở thành nút thắt cổ chai.”
🧭 1. Vấn đề với cronjob truyền thống
Hầu hết backend đều có một số tác vụ chạy theo lịch (cron):
- Gửi email hàng ngày
- Quét đơn hàng bị quá hạn
- Đồng bộ dữ liệu với hệ thống khác
Vấn đề:
- Chạy trên 1 server → không scale
- Triển khai đa node dễ bị chạy trùng job (job chạy 2 lần!)
- Không có retry, theo dõi, hoặc log rõ ràng
“Cron truyền thống rất khó kiểm soát nếu bạn chạy app dạng cluster hoặc serverless.”
🚀 2. BullMQ là gì? Vì sao phù hợp?
BullMQ là thư viện hàng đầu trong Node.js để xử lý job async, dựa trên Redis.
Ưu điểm:
- Hỗ trợ Queue, Retry, Delay, Rate limit
- Có UI quản lý (bull-board)
- Dễ dùng với NestJS, Express
- Hỗ trợ repeatable jobs → dùng làm cronjob
Redis đóng vai trò hàng đợi trung tâm → giúp phân tán job giữa các worker.
🛠️ 3. Cài đặt BullMQ
npm install bullmq ioredis
Tạo một file queue đơn giản:
// jobs/cron.job.ts
import { Queue } from 'bullmq';
import { connection } from './redis';
export const cronQueue = new Queue('cron-job', { connection });
⏰ 4. Tạo cronjob phân tán bằng repeatable job
// schedule job chạy mỗi 1 phút
await cronQueue.add('daily-task', {}, {
repeat: { cron: '*/1 * * * *' },
removeOnComplete: true,
});
Trong worker:
// workers/cron.worker.ts
import { Worker } from 'bullmq';
import { connection } from './redis';
new Worker('cron-job', async job => {
if (job.name === 'daily-task') {
console.log('Đang chạy job theo lịch...');
// Logic xử lý ở đây
}
}, { connection });
Redis sẽ đảm bảo job chỉ chạy một lần, dù có nhiều worker cùng kết nối
Syntax của cronjob:

⚙️ 5. Cấu hình nâng cao
Tính năng | Cách dùng |
---|---|
Retry job lỗi | attempts: 3 + backoff để retry khi job fail |
Delay job | delay: 60000 để trì hoãn job 1 phút |
Rate limiting | limiter: { max: 10, duration: 1000 } để tránh job chạy quá nhanh |
Priority | priority: 1 để ưu tiên job quan trọng |
Logging job | Kết hợp log + Bull Board UI để theo dõi trạng thái job dễ dàng |
🔒 6. Đảm bảo job không chạy trùng
Với BullMQ, job có thể bị lặp nếu:
- Worker bị restart giữa chừng
- Redis bị flush mất dữ liệu
Mẹo:
- Dùng
jobId
cố định để tránh tạo trùng - Luôn cấu hình
removeOnComplete: true
- Dùng lock (hoặc TTL key Redis) cho các job quan trọng
📈 7. Monitoring với Bull Board
npm install @bull-board/api @bull-board/express
Tích hợp UI vào hệ thống:
import { ExpressAdapter } from '@bull-board/express';
import { createBullBoard } from '@bull-board/api';
const serverAdapter = new ExpressAdapter();
createBullBoard({
queues: [new BullAdapter(cronQueue)],
serverAdapter,
});
app.use('/admin/queues', serverAdapter.getRouter());
Bạn sẽ có UI để theo dõi queue, job đang chạy, đã hoàn thành hay lỗi
🧪 8. So sánh với các giải pháp cron khác
Công cụ | Ưu điểm | Hạn chế |
---|---|---|
Node-Cron | Đơn giản, dễ dùng | Không phân tán, dễ chạy trùng |
Agenda | Hỗ trợ MongoDB | Ít phổ biến, ít plugin |
BullMQ | Redis-based, hỗ trợ queue phân tán | Phụ thuộc Redis |
Temporal | Workflow rất mạnh, có retry, event… | Cần thời gian học, nặng đô |
🎯 Kết luận
BullMQ + Redis là lựa chọn lý tưởng để xây dựng cronjob phân tán trong Node.js:
- Không lo chạy trùng
- Có retry, delay, log
- Dễ scale worker theo tải
- Dễ monitor với UI Bull Board
“Một hệ thống bền vững là hệ thống có job chạy đúng lúc, đúng nơi, và không chạy… hai lần.”