Why Node.js clustering is key for optimizing applications (r)

Aug 1, 2024

-sidebar-toc>        -language-notice>

This article provides a thorough look at clustering in Node.js and how it impacts the performance of an application.

What is what is a cluster?

In default, Node.js apps run in one thread. This single-threaded nature signifies that Node.js is unable to use all cores of a multi-core system that is what the majority of current systems.

Node.js can still handle multiple simultaneous requests by using non-blocking I/O operations and asynchronous programming strategies.

However, large computational workloads can block the event loop, causing the application to become unresponsive. This is why Node.js includes an native cluster module- irrespective of its single-threaded design- to take advantage of the full processing power of a multicore system.

Running multiple processes utilizes the power of multiple central processing unit (CPU) cores to enable simultaneous processing, cut down on response times, and increase throughput. This in turn improves the performance and scalability of Node.js applications.

What is the purpose of clustering?

It is the Node.js cluster module permits a Node.js application to create an array of running concurrently child processes, each handling part of the workload.

In the initialization of the module for clustering, it creates the primary process, that then splits the child process to worker process. The main process functions as a load-balancer, distributing the workload to the worker processes, while each process listens for incoming demands.

It is the Node.js cluster module comes with two options for distributingincoming connections.

  • The round-robin technique The process that is the primary one listens to an port, and accepts new connections and evenly disperses the load so that no process is overloaded. This is the default method on all operating systems except Windows.
  • Another approach is the primary method creates the listen socket and forwards it out to "interested" workers who take inincoming connections directly.

Theoretically speaking, the second method -- which is more complicated -- will yield better results. In reality, however, the distribution of the connections can be very uneven. In the Node.js manual says that 70% of connections end up in just two out of eight.

How do you cluster your Node.js applications

Let's look at the effects of clustering in a Node.js application. This tutorial uses an Express application that intentionally runs the task with a high computational load to stop the events loop.

First, run this application without clustering. Next, track the results using a benchmarking tool. After that, clustering is applied in the application, and the benchmarking process is repeated. In the end, it is time to compare results to determine how clustering improves the performance of your application.

Getting started

  1. Start by creating the project.
Mkdir cluster-tutorial
  1. Browse to the directory of your application and then create two files.      no-cluster.js     and      cluster.js      By running the command below:
CD cluster-tutorial, cd the touch no-cluster.js && cluster.js & cluster.js
  1. Initialize NPM in your project:
NPM init with a y
  1. Then, you can finally, install Express through the commands following:
npm install express

Making a non-clustered app

Within the no-cluster.js file, include the following code block:

const express = require("express");
 const PORT = 3000;
 
 const app = express();
 
 app.get("/", (req, res) => 
 res.send("Response from server");
 );
 
 app.get("/slow", (req, res) => {
 //Start timer 
 console.time("slow");
 
 // Generate a large array of random numbers
 let arr = [];
 for (let i = 0; i 

The above code block creates an express server that runs on the port 3000. It has two routes, one of which is a root ( /) route and a /slow route. The root route transmits a reply to the client with the following message "Response to server."

However, the /slow method is designed to perform intensive computation to block this loop. This route starts by triggering a timer. It then fills an array with 100,000 random numbers by using a for loop.

In the next step, with a second loop for loop is able to square every number of the created array, and then adds them together. The timer ends when this is complete, and the server responds with the result.

You can start your server by running the command below:

node no-cluster.js

You can then send a GET request at the localhost address 3000/slow.

During this time, if you attempt to make any additional requests to the server -- for instance, via it's root path ( /) -- your response will be slower because the slow route is blocking events loops.

Create a clustered app

Spawn child processes using the cluster module so that your application doesn't become unresponsive or slow down requests in the future during heavy computational tasks.

Every child process has its own event loop, which shares the port to the server of the parent, which allows more efficient use of the resources available.

First, import first the Node.js cluster and Os module to the cluster.js file. The cluster module permits the development of child processes in order to divide the work load among multiple CPU cores.

The OS module contains information regarding your computer's operating system. This module is required to determine the amount of cores you have available on your system and ensure that you don't create more child processes than cores that are available on your system.

Include the following code block to install these modules and get the total number of cores that are on your system.

const cluster = require("node:cluster");
 const numCores = require("node:os").cpus().length;

Next, you can add the block of code below in your cluster.js file:

if (cluster.isMaster) console.log(`Master $process.pid is currently running);console.log(`This machine has $numCores cores);
 
 /Fork workers. for (let i = 0; i 
 console.log(`worker $worker.process.pid died`);
 
 // Replace the dead worker
 console.log("Starting a new worker");
 cluster.fork();
 );
 

The block of code above determines whether the currently running process is a primary or a worker process. If it is it will create children processes according to the core count in your system. Then, it watches to the event of exit of the processes, and then replaces them by spawning new processes.

Finally, wrap all the relevant express logic into an else block. Your final cluster.js file should be similar to the code block that follows.

//cluster.js
 const express = require("express");
 const PORT = 3000;
 const cluster = require("node:cluster");
 const numCores = require("node:os").cpus().length;
 
 if (cluster.isMaster) 
 console.log(`Master $process.pid is running`);
 console.log(`This machine has $numCores cores`);
 
 // Fork workers. for (let i = 0; i 
 console.log(`worker $worker.process.pid died`);
 
 // Replace the dead worker
 console.log("Starting a new worker");
 cluster.fork();
 );
  else {
 const app = express();
 
 app.get("/", (req, res) => 
 res.send("Response from server");
 );
 
 app.get("/slow", (req, res) => 
 console.time("slow");
 // Generate a large array of random numbers
 let arr = [];
 for (let i = 0; i 

After implementing clustering, multiple processes can handle the requests. That means your application stays responsive during massive computational process.

What is the best way to measure performance with loadtest?

For a precise demonstration and presentation of the impact of clustering on a Node.js application, use the loadtest package from npm loadtest to evaluate the performance of your application both before and after clustering.

Use the following command to install loadtest worldwide:

Install npm the loadtest option.

It is a load test program. loadtest program will run a load test against an HTTP/WebSockets-specific URL.

After that, open the no-cluster.js file on the terminal. Open another terminal instance and run the load test as follows:

loadtest http://localhost:3000/slow -n 100 -c 10

This command sends 100 requests at a rate of 10 for your app without clustered. This command gives these results:

Non-clustered app load test results
Non-clustered app load test results.

Based on the data, it took approximately 100 seconds to complete all the requests without clustering, and the longest-running request took close to 12 seconds to complete.

Your results may vary depending the system you use.

After that, stop the no-cluster.js file and start up cluster.js. Now, open your cluster.js file on a terminal instance. Then, open another terminal instance, and perform the load test

loadtest http://localhost:3000/slow -n 100 -c 10

This command will make 100 requests with a concurrency 10 to the clustered application.

This command will produce these results:

Clustered app load test result
Test results for load testing on clustered apps.

When the app was clustered, it required 0.13 seconds (136 ms) for the app to process its requests, a huge reduction from the time the unclustered app needed. Furthermore, the longest query on the app with clustering was completed in 41 milliseconds.

This research shows that clustering significantly improves the efficiency of your application. Be sure to use PM2 or other process management tools like PM2 for managing the clustering process in your production environment.

Utilizing Node.js and's Application Host

offers various features offers a variety of features Node.js deployments, such as connections to internal databases, Cloudflare integration, GitHub deployments, as well as Google C2 Machines.

The features allow you to build and manage Node.js applications, and speed up the process of development.

When your repository has been set you can follow these steps create your Express application in :

  1. Log in or create an account in order to access the dashboard of your My dashboard.
  2. Authorize Git with the service you use.
  3. Simply click Applications on the left sidebar, and then click Add application.
  4. Choose the repository you want to deploy from and also the branch you wish to deploy from.
  5. Give a unique title to your application and select a Data center's location.
  6. Make use of all the default settings and then click to create the application.

Summary

Clustering in Node.js allows the development of multiple worker processes that can share the load, improving the efficiency and capacity of Node.js applications. Effectively implementing clustering is vital for maximizing the technique's potential.

Planning the structure and managing the allocation of resources and minimising latency in the network are essential when you implement clustering within Node.js. The complexity and importance of the implementation is one reason process managers such as PM2 are recommended for use in the production environment.

    What are your thoughts on Node.js clustering? Have you used it before? Comment below!

Jeremy Holcombe

Content and Marketing Editor at , WordPress Web Developer, and Content writer. Outside of everything related to WordPress, I enjoy the beach, golf, and watching movies. Additionally, I'm tall and have issues ;).