The Cluster Module
Since the Node.js runtime operates on a single thread, handling high-concurrency traffic requires strategies to maximize hardware utilization. The cluster module allows a Node.js application to spawn multiple child processes (workers) that share the same server port. This enables the application to distribute incoming connections across all available CPU cores.
Key Events and Methods
Events:
fork: Emitted when a new worker is spawned. Callback receives theworkerobject.online: Emitted when the worker sends a message indicating it has started.listening: Emitted when the worker callslisten(). Callback receivesworkerandaddress.disconnect: Emitted when the IPC channel is disconnected.exit: Emitted when a worker process terminates. Callback receivesworker,code, andsignal.setup: Emitted aftersetupMaster()is invoked for the first time.
Properties and Methods:
settings: Configuration object containingexec,args, andsilentproperties.isMaster: Boolean indicating if the process is the primary/master.isWorker: Boolean indicating if the process is a child/worker.setupMaster([settings]): Defines the script to be run by the workers.disconnect([callback]): Disconnects all workers and closes handles.worker: Reference to the currentWorkerobject (only in worker context).workers: A collection of active worker objects indexed byworker.id.
Worker Objects
When a worker is forked, a Worker object is created in both the master and the worker process. In the master, it acts as a handle to control the child process. In the worker, it represents the current process instance.
Events:
message: Received when the master sends data to the worker.disconnect: Triggered when the IPC channel closes.exit: Triggered when the process exits.error: Triggered on worker process errors.
Properties and Methods:
id: Unique identifier for the worker.process: The underlyingChildProcessinstance.suicide: Flag set totrueif.kill()or.disconnect()was called.send(message, [handle]): Sends a message to the master process.kill([signal]): Terminates the worker process.disconnect(): Closes servers and disconnects the IPC channel.
Implementation Example
Master Process (orchestrator.js)
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
cluster.setupMaster({ exec: 'service_worker.js' });
cluster.on('fork', (worker) => {
console.log(`Worker spawned: ${worker.id}`);
});
cluster.on('listening', (worker, addr) => {
console.log(`Worker ${worker.id} active at ${addr.address}:${addr.port}`);
});
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.id} terminated`);
});
// Limit to 4 workers or total CPU count, whichever is lower
const limit = Math.min(os.cpus().length, 4);
for (let i = 0; i < limit; i++) {
cluster.fork();
}
// Handle messages from workers
Object.values(cluster.workers).forEach(w => {
w.on('message', (msg) => console.log('Master received:', msg));
});
}
Worker Process (service_worker.js)
const cluster = require('cluster');
const http = require('http');
if (cluster.isWorker) {
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`Handled by PID: ${process.pid}`);
// Notify master process
process.send(`Request handled by ${process.pid}`);
});
server.listen(8080, () => {
console.log(`Worker PID ${process.pid} listening`);
});
}
Load Testing Client
const http = require('http');
const config = { hostname: 'localhost', port: 8080 };
function generateRequest() {
const req = http.request(config, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => console.log(data));
});
req.end();
}
// Send 5 concurrent requests
for (let i = 0; i < 5; i++) {
generateRequest();
}