Node JS is a known asynchronous-based implementation. This means it performs multiple concurrent operations at the same time on just 1 CPU core. However, this structure poses a great cause for concern and has made Node JS unsuited for some large production applications as a backend tool.
Amid all these uncertainties, Node JS clustering tools are here to the rescue.
In this tutorial, we will walk through Node JS clustering, what it entails, various tools used to cluster Node JS operations and then implement a demo project using one of the tools. This tutorial helps the reader understand how to automate their tasks and utilize the power of Node JS to ensure faster server optimization. People expected to benefit from this tutorial are;
Node JS backend developers.
DevOps engineers
Right now, we would then proceed to the tutorial but before that, I would shortly highlight the necessary prerequisites needed to fully harness all that would be discussed in this tutorial.
Intermediate Knowledge of Node JS
Basic knowledge of JavaScript operation
Knowledge of API development
Asynchronous Node JS
By default, JavaScript works synchronously, this implies, that the functions would be run and executed one after the other, thereby slowing the entire code execution. But in sharp contrast, Node JS which is built on the Chrome v8 JavaScript runtime engine is proudly asynchronous, possessing the capability to run multiple functions at the same time and achieve high performance and concurrency. This is due to its key features such as Event emitters, the async-await functionality, the use of Callback functions, Promises, generators and yield. All these make Node JS quite popular among developers and used in the development of production applications. However, a big disadvantage to the use of this backend library is its Single-threaded nature. What does it imply for a programming language to be single-threaded?
This means that there is only one CPU processing each incoming client request places each of them on a queue and deploys its memory capacity to ensure speedy completion for each request. However, its problem lies in processing very intensive client requests as seen in gaming applications.
The creation of multiple worker threads to harness CPU cores and achieve scalability tries to help navigate through this. More about the tools to navigate this is illustrated in the next section.
Concept of Load balancing
To demonstrate this concept, I would like to paint a scenario.
Let’s imagine a man decides to begin his logistics company with 1 truck. Initially, he got a few customers who patronized him and he faithfully and dutifully delivered their goods to the destination. Due to massive referrals, his customer base got larger and he was faced with the dilemma of using the same 1 truck to deliver the goods in the same delivery time he initially started with. Very soon, he began to receive negative reviews due to reduced speed and resultant inefficiency in delivery, what would you suggest he do? To buy another truck and increase operations? I would also suggest the same.
This In a nutshell mirrors what load balancing is all about. Due to the complex Node JS operation, it requires the successful completion of a task before running the others as already discussed earlier. Load balancing entails creating multiple instances of a backend service which helps split the requests into various instances already provided to help reduce request waiting time and ensure that the API experience is seamless and well-optimized.
How do we achieve this? There are quite a lot of tools made available which provide load-balancing features. Examples of these tools include;
HAProxy
NGINX
Cloud load balancer
Citrix ADC
Loadmaster
Traefik
Incapsula
Barracuda Load Balancer ADC
NeoLoad and many others.
While all these enterprise tools are very efficient, this article will be focused on Node JS's inbuilt load balancer feature, the cluster module.
Introducing the Node Cluster package
The node cluster module is a library which comes installed with the node application and can be used to run clusters of Node JS functions as multiple instances to reduce the time lag in accessing the response to those requests.
It works on the principle of creating multiple child processes known as workers which can utilize various server ports to ensure increased efficiency in processing the requests.
This cluster module supports different methods of distributing received connection requests namely,
Round-robin distribution: This operates by allotting connection requests to all servers available in an ordered arrangement. After all servers have been exhausted, the remaining requests can then be shared around each server again. This method helps to maximize full CPU capability.
Sending requests to interested workers: This method distributes incoming connection requests to active servers who are available to receive these requests.
So right now, we will be going on to demonstrate the load balancing uses of Node JS cluster by working on speeding up the time needed to access an API in our code project. Let us move on,
Demo Project
In this project, we will be running 2 JavaScript functions in which one of them would be intensive and also to assess the site speed to demonstrate the effect on the CPU core.
Here is an example of our sample code that we intend to run containing the various functions.
const express= require("express");
const app= express();
app.get("/", (req, res)=> {
res.send("Home Page")
});
app.get("/slow-page", (req, res) => {
let count = 0;
for ( let i = 0; i < 900000000; i++) {
count ++;
}
res.send(`hi its ${count}`)
})
app.listen(3000, () => {
console.log("hello")
})
In a nutshell, this code has 2 api end points with each rendering different outputs. The “/slow-page” endpoint tries to run a for loop function which is CPU intensive in order to assess the speed of the server. Running the second API endpoint just immediately before the first tends to slow down the server responses to both request its single threaded.
Attached below is the loading time for this scenario.
Now lets harness the power of Cluster module to help ensure site scalability and achieve the necessary site optimization we desire.
const express= require("express");
const app= express();
const cluster = require("cluster");
const OS = require("os")
console.log(OS.cpus().length);
if (cluster.isMaster) {
console.log(`master process ${process.pid} is running`);
cluster.fork();
cluster.fork();
}
else {
console.log(`worker ${process.pid} started`)
app.get("/", (req, res)=> {
res.send("Home Page")
});
app.get("/slow-page", (req, res) => {
let count = 0;
for ( let i = 0; i < 900000000; i++) {
count ++;
}
res.send(`hi its ${count}`)
})
app.listen(3000, () => {
console.log("hello")
})
}
The code above has us importing the inbuilt Node JS cluster module. We would then move on to initialize the cluster master function.
This can be imported and initialized as ;
const cluster = require("cluster");
const OS = require("os")
console.log(OS.cpus().length);
These lines displays the number of CPU cores on your personal computer. After initialization, the master process serves as the parent process and assigns functions to each worker. The cluster fork function establishes the formation of different child processes. The number signifies the amount of workers to be used. Running the code reveals;
Additional features:
So far, we utilized the cluster function to maximize the application speed of our web server. A great tool that also utilizes this feature is the process manager package (PM2). It provides quality UI and other features to help manage the speed and monitor the server's efficiency. You can get more info regarding it here.
Conclusion
With this, we have come to the end of the tutorial. We hope you’ve learnt essentially about Node JS asynchronous operations, cluster modules and its worker processes, and how to harness them to make server operations faster.
Feel free to drop comments and questions and also check out my other articles here. Till next time, keep on coming!