series Bee Queue Bài 2: Thực Chiến Bee-Queue - Khởi Tạo Hàng Đợi Và Worker Xử Lý Đầu Tiên
Trong mô hình Job Queue, sự tách biệt giữa Producer (nơi tiếp nhận yêu cầu) và Worker (nơi thực thi yêu cầu) là chìa khóa để hệ thống không bị "nghẽn cổ chai". Hãy tưởng tượng Producer là người nhận order món ăn tại quầy, còn Worker là đầu bếp trong nhà bếp. Họ không làm việc cùng nhau, họ chỉ giao tiếp qua "tờ hóa đơn" (Job).
1. Chuẩn bị "Vũ khí"
Trước khi code, hãy đảm bảo bạn đã cài đặt Redis trên máy (hoặc dùng Docker). Sau đó, trong thư mục dự án, hãy cài đặt các thư viện cần thiết:
npm install bee-queue
2. Producer: Người "đặt đơn hàng"
Đây là phần code sẽ nằm trong ứng dụng chính của bạn (ví dụ: API endpoint). Nó có nhiệm vụ tạo ra một công việc và đẩy vào Redis.
// producer.js
const Queue = require('bee-queue');
// Khởi tạo hàng đợi với tên là 'email-queue'
const emailQueue = new Queue('email-queue', {
redis: { host: '127.0.0.1', port: 6379 } // Cấu hình kết nối Redis
});
// Tạo một công việc (Job)
const job = emailQueue.createJob({
to: 'hieu@example.com',
subject: 'Chào mừng bạn đến với hệ thống!',
content: 'Cảm ơn bạn đã đăng ký tài khoản.'
});
// Lưu job vào Redis
job.save().then((job) => {
console.log(`Đã gửi đơn hàng thành công! Job ID: ${job.id}`);
});
Góc nhìn kỹ thuật: createJob() chỉ tạo ra cấu trúc dữ liệu, còn .save() mới là lệnh thực sự gửi nó vào hàng đợi trong Redis. Nếu không có .save(), đơn hàng của bạn sẽ không bao giờ được bếp trưởng nhìn thấy!
3. Worker: "Đầu bếp" xử lý công việc
Worker sẽ là một tiến trình (process) chạy độc lập, liên tục lắng nghe hàng đợi email-queue. Khi có job mới, nó sẽ tự động được "đánh thức".
// worker.js
const Queue = require('bee-queue');
// Khởi tạo hàng đợi với cùng tên (quan trọng!)
const emailQueue = new Queue('email-queue', {
redis: { host: '127.0.0.1', port: 6379 }
});
// Lắng nghe công việc
emailQueue.process(async (job) => {
console.log(`Đang xử lý Job ID: ${job.id} cho user: ${job.data.to}`);
// Giả lập tác vụ nặng mất 2 giây (vd: gọi API gửi mail)
await new Promise(resolve => setTimeout(resolve, 2000));
console.log(`Hoàn thành gửi email cho: ${job.data.to}`);
return { status: 'success' }; // Trả về kết quả sau khi hoàn thành
});
4. Vận hành thử nghiệm
Bạn hãy mở 2 cửa sổ Terminal:
- Terminal 1: Chạy lệnh node worker.js (Worker sẽ đứng im chờ đợi).
- Terminal 2: Chạy lệnh node producer.js.
Điều gì sẽ xảy ra?
- Ngay khi bạn chạy Producer, Worker ở Terminal 1 sẽ lập tức nhận lệnh, in ra log và thực hiện công việc (mất 2 giây), trong khi ứng dụng Producer đã kết thúc công việc của nó từ lâu.
- Đó chính là sức mạnh của bất đồng bộ! Người dùng (Producer) không phải chờ đợi "đầu bếp" (Worker) làm việc xong.
Một vài lưu ý cho "đầu bếp":
- Tính độc lập: Worker và Producer có thể nằm ở hai server khác nhau. Producer có thể viết bằng Express, Worker có thể là một process tách biệt hoàn toàn. Chỉ cần chúng trỏ chung vào một con Redis server là đủ.
- Xử lý lỗi: Nếu code bên trong process() bị throw ra một lỗi, Bee-Queue sẽ tự động đánh dấu Job đó là "thất bại" (Failed). Chúng ta sẽ học cách xử lý các Job thất bại này (Retry, Log) ở bài học sau.
Tóm lại là...
Việc cài đặt Bee-Queue cực kỳ nhanh gọn và trực quan. Bạn đã có một hệ thống Producer-Worker cơ bản. Tuy nhiên, trong môi trường thực tế, công việc không phải lúc nào cũng suôn sẻ. Sẽ có lúc Worker bị crash, Job bị lỗi, hoặc hàng đợi bị quá tải.
Ở bài học tiếp theo, chúng ta sẽ tiến tới phần "xương máu": Xử lý các Job thất bại, cơ chế Retry (thử lại) thông minh và cách theo dõi tình trạng của hàng đợi qua Events.
All Rights Reserved