The cluster module translates this sentence into English, outputting plain text directly without any additional content.
The cluster
module in Node.js allows developers to leverage the capabilities of multi-core CPUs by creating child processes (workers) to handle tasks in parallel. It follows a master-slave model, where the master process manages the child processes, and the child processes share the same port and handle requests independently, significantly improving server throughput and reliability.
Core Concepts of the cluster
Module
The core of the cluster
module lies in the division of labor between the master process and the worker processes. The master process does not handle business logic directly but is responsible for scheduling and managing the workers, while the worker processes are the units that execute the actual code. Here’s a minimal example:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master process ${process.pid} is running`);
// Fork worker processes
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker process ${worker.process.pid} has exited`);
});
} else {
// Worker processes can share any TCP connection
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World\n');
}).listen(8000);
console.log(`Worker process ${process.pid} has started`);
}
Inter-Process Communication (IPC)
Although worker processes run independently, the cluster
module provides an IPC mechanism. The master and worker processes can exchange data using process.send()
and the message
event. For example:
// Master process code
cluster.on('fork', (worker) => {
worker.send({ type: 'greeting', msg: 'Hello from master' });
});
// Worker process code
process.on('message', (msg) => {
if (msg.type === 'greeting') {
console.log(`Worker ${process.pid} received: ${msg.msg}`);
}
});
Load Balancing Strategies
By default, the cluster
module uses a round-robin strategy to distribute requests (except on Windows). Node.js allows modifying this strategy via cluster.schedulingPolicy
:
const cluster = require('cluster');
cluster.schedulingPolicy = cluster.SCHED_RR; // Round-Robin
// Or
cluster.schedulingPolicy = cluster.SCHED_NONE; // Let the OS decide
Practical Use Cases
HTTP Server Clustering
When building high-concurrency HTTP services, cluster
effectively prevents single-process blocking:
const cluster = require('cluster');
const http = require('http');
if (cluster.isMaster) {
const cpuCount = require('os').cpus().length;
for (let i = 0; i < cpuCount; i++) {
cluster.fork();
}
} else {
http.createServer((req, res) => {
// Simulate CPU-intensive task
let count = 0;
for (let i = 0; i < 1e7; i++) { count++ }
res.end(`Worker ${process.pid} handled request`);
}).listen(3000);
}
Zero-Downtime Restarts
By handling signals and managing processes, you can achieve seamless restarts:
// Master process
process.on('SIGUSR2', () => {
const workers = Object.values(cluster.workers);
const restartWorker = (i) => {
if (i >= workers.length) return;
const worker = workers[i];
worker.on('exit', () => {
if (!worker.exitedAfterDisconnect) return;
const newWorker = cluster.fork();
newWorker.on('listening', () => restartWorker(i + 1));
});
worker.disconnect();
};
restartWorker(0);
});
Advanced Features and Considerations
Shared Port Mechanism
All worker processes share the same port via the net
module's IPC channel, with connections distributed by the master process's round-robin handler. This differs from using the child_process
module directly, where each child process would need to bind to a different port.
State Sharing Issues
Due to memory isolation between workers, avoid directly modifying global state. Solutions include:
- Using external storage like Redis
- Coordinating state through the master process
- Adopting a stateless design
// Bad example: Independent counters in workers
let requestCount = 0;
http.createServer((req, res) => {
requestCount++;
res.end(`Total requests: ${requestCount}`); // Each worker has its own count
}).listen(8080);
Error Handling Best Practices
Always listen for worker exceptions; uncaught errors will cause the process to exit:
cluster.on('exit', (worker, code, signal) => {
console.error(`Worker ${worker.process.pid} died`);
if (!worker.exitedAfterDisconnect) {
cluster.fork(); // Auto-restart
}
});
// In worker processes
process.on('uncaughtException', (err) => {
console.error('Caught exception:', err);
process.exit(1); // Exit proactively for master to restart
});
Performance Optimization Tips
Controlling the Number of Processes
While workers are typically created per CPU core, I/O-intensive scenarios may benefit from more:
const totalWorkers = require('os').cpus().length * (isIOIntensive ? 1.5 : 1);
Sticky Sessions
For scenarios requiring session persistence (e.g., WebSocket), use packages like sticky-session
:
const sticky = require('sticky-session');
const server = http.createServer((req, res) => { /* ... */ });
sticky.listen(server, 3000, {
workers: 4,
env: { NODE_ENV: 'production' }
});
Comparison with Tools Like PM2
While process managers like PM2 also offer clustering, the cluster
module has advantages:
- No additional dependencies
- Finer control over logic
- Deep integration with the Node.js runtime
Typical PM2 cluster configuration:
pm2 start app.js -i max # Automatically starts a cluster based on CPU cores
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:child_process模块
下一篇:进程间通信