How to Setup a Node.js Cluster for Speed & Stability

Updated: December 28, 2023 By: Guest Contributor Post a comment

Learn how to setup a Node.js cluster to improve the speed and stability of your project.

Overview

Node.js is a powerful JavaScript runtime built on Chrome’s V8 engine. It is designed to build scalable network applications. However, Node.js runs in a single thread by default. In a multi-core system, this means that the full computing power of the server is not utilized. A Node.js cluster module allows you to create child processes that run simultaneously and share the same server port. This tutorial will guide you through setting up a Node.js cluster for improved speed and stability.

Before starting with clustering, ensure you have Node.js installed in your system. You can download it from the official Node.js website.

Getting Started with Node Clustering

To demonstrate how clustering works, we’ll start with a simple example. Create a new JavaScript file named ‘cluster.js’. The first step is to require the cluster module and the os module, which we’ll use to determine the number of CPU cores.

const cluster = require('cluster');
const os = require('os');

const numCPUs = os.cpus().length;

In the next step, we’ll check if the current process is the master process or a child process (worker). If it’s the master process, we’ll fork new workers; else, we’ll start our HTTP server.

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
  // In this case it is an HTTP server
  const http = require('http');

  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello world!');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

This code starts an instance of an HTTP server for each CPU core. When a worker dies, it logs the event to the console. This is a basic setup without any fault-tolerance or load-balancing logic.

Handling Worker Crashes

To ensure stability, your cluster should automatically replace workers that crash. Update the ‘exit’ event listener to restart workers.

cluster.on('exit', (worker, code, signal) => {
  console.log(`worker ${worker.process.pid} died`);
  console.log('Starting a new worker');
  cluster.fork();
});

Advanced Clustering with Express

For a more real-world scenario, we’ll create an Express.js application with clustering. First, install Express in your project directory.

npm install express

Now, modify your ‘cluster.js’ to use an Express application instead of a plain HTTP server.

const express = require('express');

if (cluster.isMaster) {
  // ... other codes ...
} else {
  const app = express();

  app.get('/', (req, res) => {
    res.send('Hello from Worker ' + process.pid);
  });

  app.listen(8000);

  // ... rest of the worker code ...
}

Load Balancing

The cluster module uses a round-robin approach by default on all operating systems except Windows. To custom-tailor load balancing or enable it on Windows, you can use a reverse proxy like Nginx.

Here is how you can set up Nginx to proxy requests to your Node.js cluster:

// Nginx configuration example
upstream node_cluster {
  server 127.0.0.1:8000;
  server 127.0.0.1:8001;
  // add other ports if required
}

server {
  listen 80;

  location / {
    proxy_pass http://node_cluster;
    // add other proxy settings as needed
  }
}

Graceful Shutdown

To avoid dropped connections during a shutdown, you should implement a graceful shutdown for your workers. Update your worker code with the following:

process.on('SIGTERM', () => {
  console.log(`Worker ${process.pid} is shutting down`);
  server.close(() => {
    process.exit(0);
  });

  // Timeout to close the worker forcibly
  setTimeout(() => {
    process.exit(1);
  }, 10000);
});

Conclusion

In conclusion, leveraging a Node.js cluster can considerably improve the speed and stability of your application by taking advantage of multiple CPU cores. This guide covered the basic to advanced steps in creating a Node.js cluster, from forking workers to implementing load balancing and graceful shutdown. As a best practice, always test the clustering setup in a staging environment before moving to production and monitor the application for any potential issues that may arise. With these principles, you can ensure that your Node.js applications are scalable and robust.