Maximize Efficiency Using Node.js Clustering on Multi-Core Systems
Harness the Power of Multi-Core Processors to Boost Node.js Application Performance

Introduction
"In Node.js, Clustering is a technique that allows the utilisation of hardware based on a multi-core processor."
Node.js, by default, follows the single-threaded event loop-based architecture.
Understanding the Problem
Node.js excels at handling asynchronous I/O operations but struggles with CPU-intensive tasks.
In CPU-heavy scenarios, the single-threaded architecture can cause performance issues, making the entire application unresponsive.
Despite having multiple CPU cores, Node.js does not utilize all of them by default.
Node.js uses only one CPU core for the main thread that handles the event loop.
For example, on a four-core system, Node.js will only use one core by default.
The Solution: Node.js Clustering
- Node.js clustering allows you to create multiple instances of your application, with each instance running independently as a worker process. These worker processes share the same port but utilize different CPU cores.
Cluster
Clusters of Node.js processes can be used to run multiple instances of Node.js that can distribute workloads among their application threads.
1. Key Components:
Master/ Parent Process: Oversees the cluster and handles process management tasks.
Worker/ Child Processes: Replicas of the main application, each executing in its own isolated environment.
Node.js includes a built-in
clustermodule.The cluster module enables the creation of child processes, which are copies of your program running concurrently on the same server port.
Each child process has its own:
event loop,
V8 instance,
and memory.
A parent process routes traffic to these child processes.
There are two ways in which the traffic is routed.
One is based on the round-robin technique. In this, the load is equally shared among the child processes.
The second method involves the sending of work to the interested child processes.
Child processes communicate with the parent process using inter-process communication.
On a 4-core machine, using the cluster module will create four copies of your program, allowing you to handle four times the normal traffic at the same speed.
Consequently, clustering can significantly enhance the performance of your Node.js application.
2. How it Works?
Master Process Creation: When a Node.js application starts with clustering enabled, a master process is initiated.
Worker Process Forking: The master process forks multiple worker processes, typically one per available CPU core.
Load Balancing: The master process distributes incoming connections across the worker processes, ensuring efficient workload distribution.
Communication: Worker processes communicate with the master process and other workers using inter-process communication (IPC).
Process Management: The master process monitors worker processes and restarts them if they fail or become unresponsive.
3. A look at Load Balancing Server:
Node.js cluster module can be said to provide the load balancing server.
Consider a scenario where a large synchronous operation is being handled. The event loop will occupy the synchronous part of all processes, causing other requests to queue up and increasing wait times.
Multiple processes can reduce this wait time and improve performance.
When multiple processes are running, other processes can handle incoming requests if one is busy with a CPU-intensive task. This ensures the utilization of multiple cores.
Thus, the cluster module enables load sharing among child processes, preventing the application from becoming unresponsive.
4. Advantages of Clustering:
Load Balancing: Distributes processing across all CPU cores, improving system throughput.
Fault Tolerance: If a worker process crashes, the master process can automatically restart it, ensuring high availability.
Seamless Updates: Worker processes can be restarted one at a time, minimizing downtime.
Automatic Recovery: New processes can replace failed ones immediately without manual intervention.
Efficient Resource Utilization: Maximizes the use of hardware resources, reducing wastage.
Example
Now let us have a look at how you can use the cluster module to improve the performance of our application. We will create two applications, one with clustering and one without clustering.
1. Without Clustering:
Let's start by creating a simple Node.js application without using the cluster module. This application will handle incoming requests on a single process, utilizing only one CPU core.

Here's an example of how you can set it up:
const express = require("express");
const PORT = 3000;
const app = express();
// Display the process ID of the current worker.
console.log(`worker pid=${process.pid}`);
// Define a GET route for "/nocluster"
app.get("/nocluster", (req, res) => {
console.time("API-without-cluster"); // Start a timer for the API request
let result = 0;
for (let i = 0; i <= Math.pow(9, 7); i++) {
// Loop from 0 to 9^7
result = result + i + Math.pow(i, 10); // Perform a complex calculation
}
// Stop the timer and log the elapsed time for the operation
console.timeEnd("API-without-cluster");
console.log(`RESULT IS ${result} - from PROCESS ${process.pid}`);
// Send the result as the response
res.send(`Result number is ${result}`);
});
// Start the server and listen on the specified port
app.listen(PORT, () => {
console.log(`App listening on port: ${PORT}`);
});
Output : (will vary)
worker pid=14498
App listening on port: 3000
API-without-cluster: 439.67ms
RESULT IS 2.7244637954262133e+72 - from PROCESS 14498
What happens behind the scenes:
When you execute the
without-clustering.jsfile using thenodecommand, the operating system (OS) initiates a process.The OS assigns memory to the program and creates an entry in its process list, which includes all active processes. This entry is identified by a process ID.
The program's binary is then found and loaded into the allocated memory.
Once loaded, the program begins execution.
During its execution, the process operates independently, unaware of other processes in the system, and its activities do not impact other processes.
With a single process handling the Node.js application on a multi-CPU server, it will manage all incoming requests on its own.
2. With Clustering :
In this step, you will add the cluster module to create multiple instances of the same program to handle more load and improve performance.

In this case the requests go through the load balancer in the primary process, which then uses the round-robin algorithm to distribute the requests among the processes.
const express = require("express");
const PORT = 3000;
const cluster = require("cluster");
const totalCPUs = require("os").cpus().length;
if (cluster.isMaster) {
// Display the number of available CPU cores
console.log(`Number of CPUs is ${totalCPUs}`);
console.log(`Master ${process.pid} is running`);
// Fork worker processes equal to the number of CPU cores
for (let i = 0; i < totalCPUs; i++) {
cluster.fork();
}
// Listen for the 'exit' event on workers to restart them if they die
cluster.on("exit", (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
console.log("Starting/fork another worker!");
cluster.fork();
});
} else {
// Worker processes share the same server code
start();
}
function start() {
const app = express();
console.log(`Worker with id ${process.pid} started`);
app.get("/cluster", function (req, res) {
console.time("API-with-cluster"); // Start a timer to measure the request duration
let result = 0;
// Iterate 9^7 times.
for (let i = Math.pow(9, 7); i >= 0; i--) {
// Perform a complex operation.
result = result + i + Math.pow(i, 10);
}
console.timeEnd("API-with-cluster"); // Stop the timer and log the elapsed time for the computation
console.log(`Result is ${result} - from PROCESS ${process.pid}`);
res.send(`Result number is ${result}`);
});
app.listen(PORT, () => {
console.log(`App listening on PORT : ${PORT}`);
});
}
What happens behind the scenes:
When you run the clustered version of the application, the primary process (master) is initiated.
The master process detects the number of CPU cores available, which in this case is 4.
The master process then forks 4 child processes (workers), each running an instance of the application.
Each child process listens on the same port, 3000, but operates independently.
The master process uses a load balancing algorithm, such as round-robin, to distribute incoming requests among the 4 child processes.
As a result, the clustered version can handle a higher number of concurrent requests.
The overall processing time is reduced because the workload is distributed across multiple processes, utilizing all available CPU cores efficiently.
Conclusion:
Node.js uses a single-threaded event loop by default.
The built-in cluster module allows the creation of child processes.
Each child process is a replica of the main program with its own event loop, V8 instance, and memory.
Clustering provides load balancing and parallel processing.
Multiple processes can handle incoming requests if one is busy with a CPU-intensive task.
Application performance improves significantly when the load is distributed across multiple cores.
A master process manages load distribution by routing traffic to child processes.
Traffic can be routed using either the round-robin technique or by sending work to interested child processes.
Follow Me:
Stay connected and follow me on social media for more updates, articles, and insights on Node.js and other technologies:
Feel free to reach out, connect, and share your thoughts!




