
Chào các dân chơi Node.js! Anh Creyt lại lên sóng đây, hôm nay mình cùng nhau 'mổ xẻ' một khái niệm nghe có vẻ hơi 'pro' nhưng thực ra lại cực kỳ 'chill' và quan trọng để app của chúng ta không bị 'đơ' khi lượng request tăng đột biến: cluster.isMaster.
Node.js và Cú Lừa 'Single Threaded' Hay 'Tại Sao App Của Tụi Mày Chỉ Dùng Có 1 Core CPU?'
Ok, đầu tiên, phải nói thẳng một sự thật 'phũ phàng' mà nhiều bạn mới vào nghề Node.js hay bị lầm tưởng. Node.js nổi tiếng là 'single-threaded' (đơn luồng) cho việc xử lý JavaScript. Điều này có nghĩa là, về cơ bản, một process Node.js chỉ chạy trên một nhân CPU duy nhất tại một thời điểm. Nghe có vẻ 'ghẻ' đúng không? Trong khi con máy 'gaming gear' của bạn có 4, 8, thậm chí 16 nhân CPU cơ mà!
Nó giống như bạn có một căn bếp xịn xò với 8 cái bếp từ, nhưng lại chỉ có một đầu bếp siêu đẳng (chính là event loop của Node.js) đứng nấu tất cả các món. Anh ta nhanh thật đấy, nhưng nếu có 8 cái nồi cùng lúc cần đảo, chiên, xào, anh ta cũng chỉ xử lý từng cái một. Trong khi đó, 7 cái bếp kia thì ngồi chơi xơi nước!
Vậy làm sao để 'flex' được hết sức mạnh của con CPU đa nhân kia? Câu trả lời chính là: Node.js Cluster!
cluster.isMaster - Vị 'Quản Lý' Tài Ba Của Đội Quân 'Workers'
Module cluster trong Node.js sinh ra để giải quyết bài toán kia. Nó cho phép bạn chạy nhiều instance (bản sao) của ứng dụng Node.js của mình trên cùng một cổng mạng (port), và mỗi instance này sẽ chạy trên một nhân CPU riêng biệt (hoặc ít nhất là có cơ hội được chạy). Nó biến căn bếp một đầu bếp thành một 'nhà hàng' với nhiều đầu bếp cùng làm việc, chia sẻ workload.
Trong cái 'nhà hàng' này, sẽ có một thằng làm 'boss', làm 'quản lý', và những thằng còn lại là 'nhân viên' hay còn gọi là 'workers'. Và đó chính là lúc cluster.isMaster tỏa sáng!
cluster.isMaster (hay cluster.isPrimary từ Node.js v16 trở đi, nhưng anh Creyt sẽ dùng isMaster theo yêu cầu của tụi bây) là một thuộc tính boolean (true/false) của module cluster. Nó dùng để kiểm tra xem process Node.js hiện tại có phải là process 'master' (chủ đạo) hay không.
-
Nếu
cluster.isMasterlàtrue: Đây chính là process 'master'. Nhiệm vụ của nó không phải là xử lý các request HTTP hay chạy logic nghiệp vụ của ứng dụng. Nó là 'CEO' của công ty, chỉ lo việc quản lý, điều phối, và 'thuê' (fork) các 'nhân viên' (workers) để làm việc thật. Nó cũng sẽ 'giám sát' các nhân viên, nếu thằng nào 'tạch' (crash), nó sẽ 'tuyển' thằng mới thay thế. -
Nếu
cluster.isMasterlàfalse: Đây chính là một process 'worker'. Các worker này mới là những 'người lính' thực thụ, chạy code ứng dụng của bạn (ví dụ: một server Express, một API service), và trực tiếp nhận và xử lý các request từ người dùng. Mỗi worker có thể chạy trên một nhân CPU khác nhau, giúp tận dụng tối đa tài nguyên máy chủ.
Tóm lại: cluster.isMaster giúp bạn phân biệt rõ ràng vai trò của từng process trong một ứng dụng Node.js được phân cụm (clustered). Một thằng 'boss' lo quản lý, nhiều thằng 'lính' lo làm việc.

Code Ví Dụ Minh Hoạ: 'Nghệ Thuật' Chia Việc!
Để dễ hình dung, anh Creyt sẽ cho tụi bây xem một ví dụ kinh điển về việc dùng cluster để tạo một server HTTP đơn giản nhưng có thể tận dụng đa nhân CPU.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // Lấy số lượng nhân CPU
// Cổng mà server sẽ lắng nghe
const PORT = 3000;
if (cluster.isMaster) {
// Đây là process 'master' (CEO)
console.log(`Master process ${process.pid} is running`);
// 'Tuyển' (fork) các 'nhân viên' (workers) bằng số lượng nhân CPU
for (let i = 0; i < numCPUs; i++) {
cluster.fork(); // Mỗi lần fork() sẽ tạo ra một worker process mới
}
// Lắng nghe sự kiện 'exit' từ các worker. Nếu worker nào 'tạch', fork thằng mới!
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died with code ${code}, signal ${signal}`);
console.log('Starting a new worker...');
cluster.fork(); // Khởi động lại worker bị chết
});
} else {
// Đây là một process 'worker' (Nhân viên)
// Worker sẽ chạy server HTTP thực sự
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from Worker ${process.pid}!`);
// Thử nghiệm lỗi để xem master có khởi động lại worker không
// if (Math.random() < 0.1) {
// console.log(`Worker ${process.pid} is crashing!`);
// process.exit(1);
// }
}).listen(PORT, () => {
console.log(`Worker ${process.pid} started and listening on port ${PORT}`);
});
}
Cách chạy:
- Lưu đoạn code trên vào file
app.js. - Mở terminal và chạy:
node app.js - Mở trình duyệt hoặc dùng
curlđể truy cậphttp://localhost:3000vài lần.
Bạn sẽ thấy trong console, process master sẽ khởi động các worker. Mỗi lần bạn refresh trình duyệt, một worker khác nhau (với process.pid khác nhau) có thể trả lời, chứng tỏ các request đang được chia đều. Nếu bạn bỏ comment đoạn code gây lỗi ngẫu nhiên trong worker, bạn sẽ thấy master tự động khởi động lại worker khi nó crash.
Mẹo 'Chơi' cluster Như Một 'Pro' (Best Practices)
- Một Worker Một Core: Thường thì, số lượng worker bằng với số lượng nhân CPU vật lý là tối ưu nhất. Nhiều quá sẽ gây overhead, ít quá thì lãng phí tài nguyên.
- Workers Phải 'Stateless': Điều này cực kỳ quan trọng! Các worker không nên lưu trữ dữ liệu quan trọng, phiên làm việc (session) hay trạng thái (state) cục bộ. Nếu một worker chết, dữ liệu đó sẽ mất. Hãy lưu trữ state ở những nơi tập trung như Redis, MongoDB, PostgreSQL... để worker nào cũng có thể truy cập.
- Tắt Server 'Duyên Dáng' (Graceful Shutdown): Khi bạn muốn tắt server (ví dụ: để deploy phiên bản mới), đừng 'kill' thẳng tay các worker. Hãy gửi tín hiệu
SIGTERMđể worker có thời gian hoàn thành các request đang xử lý rồi mới thoát. Master có thể đợi các worker hoàn thành trước khi tự thoát. - Log Tập Trung: Khi có nhiều worker, mỗi worker sẽ in log riêng. Hãy dùng các thư viện logging chuyên nghiệp (như Winston, Pino) và cấu hình để chúng gửi log về một nơi tập trung (file, ELK stack, CloudWatch...) để dễ dàng debug và giám sát.
- Giám Sát Liên Tục: Luôn theo dõi hiệu suất và tình trạng của các worker. Các công cụ như PM2 (Process Manager 2) có thể giúp bạn quản lý và giám sát cluster một cách hiệu quả hơn rất nhiều.
Ứng Dụng Thực Tế: Ai Đang 'Flex' Với cluster?
Hầu hết các ứng dụng Node.js lớn, có lượng truy cập cao đều sử dụng hoặc tận dụng cơ chế clustering một cách gián tiếp. Ví dụ:
- Các API Backend: Những API phục vụ hàng triệu người dùng mỗi ngày cần khả năng chịu tải và tốc độ xử lý cao.
clustergiúp chúng tận dụng tối đa sức mạnh của server. - Ứng dụng Real-time (Socket.IO): Mặc dù Socket.IO cần một chút cấu hình đặc biệt để hoạt động với cluster (dùng adapter như
socket.io-redis), nhưng việc chạy nó trên nhiều worker giúp tăng khả năng xử lý kết nối đồng thời. - Microservices: Trong kiến trúc microservices, mỗi service có thể là một ứng dụng Node.js độc lập. Việc chạy mỗi service với
clustertrên một server giúp tối ưu tài nguyên.
Các website hay ứng dụng như Netflix (một phần backend), LinkedIn (một số dịch vụ), Trello (phần real-time) đều có thể đang dùng Node.js và các kỹ thuật scaling tương tự cluster để đảm bảo hệ thống luôn mượt mà.
Thử Nghiệm Của Anh Creyt & Nên Dùng Cho Case Nào?
Anh Creyt nhớ có lần, hồi mới 'chân ướt chân ráo' làm một con API cho một dự án 'khủng', app cứ 'chết ngắc' khi có vài trăm request đồng thời. Lúc đó, CPU usage chỉ loanh quanh 25% (trên máy 4 core), nhưng response time thì 'lề mề' như rùa bò. Sau khi 'ngâm cứu' và áp dụng cluster, CPU usage nhảy vọt lên 90-100% (của tất cả các core), và response time thì 'nhanh như chớp'! Đó là lúc anh nhận ra sức mạnh của việc chia sẻ công việc.
Nên dùng cluster khi:
- App của bạn là 'CPU-bound': Tức là nó tốn nhiều tài nguyên CPU để xử lý các tác vụ tính toán, mã hóa, giải mã, nén ảnh... và bạn muốn tận dụng hết các nhân CPU của server.
- Bạn muốn tăng 'throughput' (số lượng request xử lý được trong một khoảng thời gian): Càng nhiều worker, càng nhiều request có thể được xử lý song song.
- Bạn cần tăng 'availability' (khả năng sẵn sàng): Nếu một worker bị crash, các worker khác vẫn tiếp tục hoạt động, và master sẽ khởi động lại worker mới, giảm thiểu thời gian downtime.
- Bạn đang chạy ứng dụng Node.js trên một server vật lý hoặc VM đơn lẻ và muốn 'vắt kiệt' hiệu năng của nó.
Không nên quá 'lạm dụng' hoặc cần cân nhắc khi:
- App của bạn là 'I/O-bound': Tức là nó dành phần lớn thời gian chờ đợi các thao tác đọc/ghi file, gọi database, gọi API bên ngoài. Node.js vốn đã rất giỏi xử lý I/O bất đồng bộ, nên việc thêm cluster có thể không mang lại nhiều lợi ích đột phá như với CPU-bound, đôi khi còn tăng overhead.
- Bạn đã dùng các công cụ quản lý process như PM2: PM2 đã tích hợp sẵn cơ chế cluster mode rất mạnh mẽ và dễ sử dụng. Nó sẽ tự động quản lý các worker và
isMastercho bạn. Trong trường hợp này, bạn chỉ cần viết code ứng dụng thông thường và để PM2 lo phần clustering. - Bạn đang chạy trong môi trường container hóa (Docker, Kubernetes): Các hệ thống này thường có cơ chế scale ngang (horizontal scaling) ở cấp độ container, tức là bạn sẽ chạy nhiều container Node.js độc lập và dùng load balancer phía trước. Trong trường hợp này, việc dùng
clusterbên trong mỗi container có thể là overkill hoặc không cần thiết, vì mỗi container có thể đã được gán một lượng CPU nhất định. Tuy nhiên, nếu bạn muốn mỗi container tận dụng hết các core CPU được cấp phát,clustervẫn có thể hữu ích.
Vậy đó, cluster.isMaster không chỉ là một thuộc tính, nó là 'kim chỉ nam' giúp bạn xây dựng những ứng dụng Node.js 'bất tử' và 'khủng bố' hơn nhiều trên con server của mình. Hãy thử và cảm nhận sức mạnh của nó nhé!
Thuộc Series: Nodejs
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!