Sling Academy
Home/Node.js/How to Setup a Node.js Cluster for Speed & Stability

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

Last updated: December 28, 2023

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.

Next Article: Setting Up Docker Compose with Node.js, Express, and TypeScript

Previous Article: Node.js with Docker Compose, Express, Mongoose, and MongoDB

Series: Node.js Intermediate Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates