DEV Community

Sarva Bharan
Sarva Bharan

Posted on

Scaling Node.js with the Cluster Module

The Cluster module allows Node.js to leverage multi-core systems, improving app performance. Let's explore how to use it effectively.

Why Cluster?

  1. Utilize all CPU cores
  2. Improve app responsiveness
  3. Increase reliability through worker redundancy

Basic Usage

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
  });
} else {
  // Workers can share any TCP connection
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}
Enter fullscreen mode Exit fullscreen mode

Load Balancing

Node.js handles load balancing automatically using a round-robin approach.

Inter-Process Communication (IPC)

if (cluster.isMaster) {
  const worker = cluster.fork();
  worker.send('Hi there');
} else {
  process.on('message', (msg) => {
    console.log('Message from master:', msg);
  });
}
Enter fullscreen mode Exit fullscreen mode

Zero-Downtime Restarts

if (cluster.isMaster) {
  cluster.on('exit', (worker, code, signal) => {
    if (!worker.exitedAfterDisconnect) {
      console.log('Worker crashed. Starting a new worker');
      cluster.fork();
    }
  });

  process.on('SIGUSR2', () => {
    const workers = Object.values(cluster.workers);
    const restartWorker = (workerIndex) => {
      const worker = workers[workerIndex];
      if (!worker) return;

      worker.on('exit', () => {
        if (!worker.exitedAfterDisconnect) return;
        console.log(`Exited process ${worker.process.pid}`);
        cluster.fork().on('listening', () => {
          restartWorker(workerIndex + 1);
        });
      });

      worker.disconnect();
    };

    restartWorker(0);
  });
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Use worker_threads for CPU-intensive tasks
  2. Implement proper error handling in workers
  3. Monitor worker health and restart if necessary
  4. Use a process manager like PM2 for production

Pitfalls to Avoid

  1. Sharing server handles explicitly (Node.js does this automatically)
  2. Overusing IPC (can become a bottleneck)
  3. Neglecting to handle worker crashes

Cluster module is powerful for horizontal scaling, but use judiciously. Always profile to ensure it's solving your specific performance needs.

Cheers🥂

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

đź‘‹ Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay