Prerequisites
Basic understanding of JavaScript
Know how to create a basic express server
Introduction
In this article, we will discuss how to do vertical scaling in NodeJS. But first, we need to understand what scaling means to grasp the concept of vertical scaling.
Scaling a server
Scaling the server is a strategy to handle increased load and ensure your server can manage higher traffic volumes efficiently. This involves optimizing resources such as CPU, memory, and storage to enhance the server's performance and reliability under heavy usage.
Understanding Vertical Scaling
Vertical scaling, also known as "scaling up," refers to the process of increasing the resources (CPU, RAM, storage, etc.) of a single server or node to handle higher loads and more complex tasks. This approach is often simpler to implement compared to horizontal scaling, which involves adding more servers or nodes to distribute the workload.
Why Nodejs does not support Vertical scaling
As shown in the diagram above NodeJS never utilize the full resources of your CPUs. So even if you think that by just scaling up the size of a server for example : Scaling your AWS ec2 instance from t3-large → T3-xlarge will only increase your server bill not the performance of your application Because of the bellow mentioned reasons.
Single-Threaded Event Loop
Node.js is single-threaded by default and uses an event-driven, non-blocking I/O model. This means it runs on a single thread and relies on callbacks to handle concurrent requests
Limited Memory Usage
By default, Node.js limits itself to using 1.76GB of memory on 64-bit machines. Even if you have a server with 32GB of RAM, the Node process will only consume a fraction of it
NodeJS provide an inbuild module to handle Multi-threading named Cluster
Example
Express code that we want to run on multiple threads
const express = require("express");
const PORT = 3000;
const app = express();
app.get("/", (req, res) => {
// Some Task
let count = 0;
while (count < 1000000000) {
count++;
}
return res.status(200).json({
message: `Hello from the express Server ${process.pid} final count is ${count}`,
});
});
app.listen(PORT, () => {
console.log(`server is running at ${PORT}`);
});
In order to run your code on multiple thread you need to know how many cores you have in you computer
const os = require("os");
const NumberOfCpu = os.cpus().length;
Now we can run our code on multiple threads using the cluster module. cluster.isPrimary
tells us whether we are on the default thread or not. When we are on the primary thread, we will create more threads. If we are on a non-primary thread, we will run our Express server code.
const express = require("express");
const cluster = require("cluster");
const os from "os";
const NumberOfCpu = os.cpus().length;
if (cluster.isPrimary) {
console.log(`Number of CPUs is ${totalCPUs}`);
console.log(`Main ${process.pid} is running`);
// Forking new worker threads.
for (let i = 0; i < NumberOfCpu; i++) {
cluster.fork();
}
// if threads one stop working this code will run new thread
cluster.on("exit", (worker, code, signal) => {
console.log(`worker ${worker.process.pid} terminated`);
console.log("Let's fork another worker!");
cluster.fork();
});
} else {
// Your Express Server Code
}
Full Example Code
import express from "express";
import cluster from "cluster";
import os from "os";
const NumberOfCpu = os.cpus().length;
if (cluster.isPrimary) {
console.log(`Number of CPUs is ${NumberOfCpu}`);
console.log(`Main ${process.pid} is running`);
// Forking new worker threads.
for (let i = 0; i < NumberOfCpu; i++) {
cluster.fork();
}
// if threads one stop working this code will run new thread
cluster.on("exit", (worker, code, signal) => {
console.log(`worker ${worker.process.pid} terminated`);
console.log("Let's fork another worker!");
cluster.fork();
});
} else {
const PORT = 3000;
const app = express();
app.get("/", (req, res) => {
// Some Task
let count = 0;
while (count < 1000000000) {
count++;
}
return res.status(200).json({
message: `Hello from the express Server ${process.pid} final count is ${count}`,
});
});
app.listen(PORT, () => {
console.log(`server is running at ${PORT} and thread ${process.pid}`);
});
}