Đã bao giờ bạn gặp cảnh server Node.js đang chạy mượt mà, bỗng nhiên có một user bấm upload file Excel 500MB để xử lý, và... bùm! Cả hệ thống gần như đứng hình. Những user khác bấm tải lại trang liên tục nhưng giao diện chỉ xoay đều trong vô vọng. Nếu bạn từng toát mồ hôi hột vì tình huống này, thì đã đến lúc chúng ta cần một giải pháp mạnh tay hơn.
Nỗi oan của Event Loop "đơn độc"
Lâu nay chúng ta luôn nghe khen Node.js xử lý hàng ngàn request cùng lúc cực kỳ tốt nhờ cơ chế bất đồng bộ (asynchronous). Điều này đúng, nhưng chỉ đúng với các tác vụ liên quan đến mạng (I/O) như gọi API hay đọc ghi database. Bản chất Node.js vẫn là một kiến trúc đơn luồng (single-thread), nghĩa là nó chỉ có đúng một nhân viên làm việc chính ở quầy tiếp tân.

Khi bạn bắt nhân viên này phải tự tay ngồi tính toán một thuật toán phức tạp, resize một bức ảnh độ phân giải 4K, hay parse một file dữ liệu khổng lồ, họ sẽ phải dừng việc tiếp khách lại. Hậu quả là mọi request khác đều bị chặn đứng (blocking). Ngay cả những quá trình xử lý nền tảng như cách JWT Authentication giúp bạn "bảo mật" API và thoát cảnh server "mất trí nhớ" cũng sẽ bị đình trệ, khiến user đang dùng thì văng khỏi hệ thống.
"Phân thân" server với Worker Threads
Để giải cứu Event Loop khỏi sự cô đơn và quá tải, Node.js đã chính thức tung ra module Worker Threads. Hiểu đơn giản, tính năng này cho phép bạn tạo ra thêm các "nhân viên phụ" chạy song song trên các luồng (threads) khác nhau. Quầy tiếp tân vẫn tiếp khách mượt mà, còn việc nặng nhọc đã có đội hậu cần lo.
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
// Luồng chính (Quầy tiếp tân)
console.log('Đang tạo luồng phụ để xử lý file nặng...');
const worker = new Worker(__filename);
worker.on('message', (result) => {
console.log(`Đã xử lý xong: ${result}`);
});
} else {
// Luồng phụ (Đội hậu cần)
let count = 0;
for (let i = 0; i < 1000000000; i++) {
count++; // Giả lập vòng lặp tính toán cực nặng
}
parentPort.postMessage(count);
}Đoạn code trên chính là chìa khóa. Thay vì để vòng lặp tỷ bước kia khóa chặt server, chúng ta ném nó sang một file riêng biệt hoặc một khối lệnh phụ. Luồng chính vẫn rảnh rang để nhận request mới, server không còn hiện tượng "treo máy" nữa.
Khi nào thì nên "rút kiếm"?
Mẹo nhỏ: Đừng lạm dụng Node.js Worker Threads cho mọi thứ. Việc tạo ra một thread mới tốn khá nhiều tài nguyên hệ thống và bộ nhớ. Chỉ dùng chúng cho các tác vụ tốn CPU thật sự nặng.
Nếu bạn chỉ đang chọc vào database để lấy bài viết, đừng dùng Worker Threads. Nhưng nếu bạn đang xây dựng một tính năng xuất báo cáo PDF tự động, xử lý video, hay đào tiền ảo bằng trình duyệt (tất nhiên là đừng làm trò này), thì đây là lúc vũ khí này phát huy sức mạnh. Bạn có thể đã biết cách Environment Variables giúp bạn "giữ bí mật" dự án và thoát cảnh lộ API key, thì việc bảo vệ server khỏi những "cú đấm" CPU cũng là một kỹ năng sống còn của DevOps.
Để đào sâu hơn về tư duy kiến trúc và làm chủ mọi ngóc ngách của backend, bạn có thể tham khảo các lộ trình học lập trình bài bản tại DIA DEMY. Rất nhiều bí kíp tối ưu hệ thống đang chờ bạn khám phá đấy.
Sóng gió chưa dừng lại ở đây
Giờ đây, bạn đã biết cách chia nhỏ công việc để server không bị đơ khi gặp tác vụ nặng. Nhưng bài toán hiệu suất chưa bao giờ đơn giản như vậy. Điều gì sẽ xảy ra nếu tác vụ không nặng, mà số lượng user truy cập lại tăng đột biến gấp 100 lần trong vài giây? Worker Threads sẽ không cứu được bạn trong trường hợp cạn kiệt RAM. Chúng ta sẽ cần bàn đến nghệ thuật "nhân bản" toàn bộ ứng dụng bằng Cluster Mode ở những câu chuyện sau!




Vui lòng đăng nhập để bình luận.