Application Scaling in Node.js
Scaling of Node.js applications can be possible with these concepts Child processes, the cluster module, load balancing, availability, and monitoring.
The Node child process module is a good concept for scaling NodeJs applications. It allows you to fork a new Node instance noting but child process, ideally in another CPU core, and offload the heavy task to the newly forked child process. This keeps the parent process free for new requests, and the results of the offloaded task can be triggered back to the parent process once the task is done.
Let’s check the following example.
// receive messages from the parent process process.on("msg", msg => { let isPrime = checkIfPrime(msg); // send the results to the parent process process.send(isPrime); // kill child process process.exit(); })
function checkIfPrime(number){ let isPrime = true; for (let j = 2; j< number; j++){ if(number % j === 0){ isPrime = false; } } return isPrime && number > 1; }
The below code is for the Node server to execute the above function using a newly forked child process once the API endpoint is called.
const childProcess = require('child_process') const express = require('express') const bodyParser = require('body-parser') const app = express(); app.use(bodyParser.json());
app.post("/paresentProcess", (req,res) => { const forked_child_process = childProcess.fork('./checkIfPrime.js'); // sending msg to the child process forked_child_process.send(parseInt(req.body.number)); // response receivin from the child process forked_child_process.on("msg", isTheNumberPrime => res.send(isTheNumberPrime)); })
app.listen(3000, () => { console.log('server up on port 3000'); })
Handling a Larger Number of Requests
Being a node js single-threaded can become a limitation when serving a higher number of requests. As the number of requests increases, our server’s response time can take longer time because all the requests are served using only one thread.
The Node cluster module is a solution for the above issue. It allows you to have multiple Node instances(multi-threads) following the same execution flow, listening on the same port for all the instances, which makes it possible to handle a larger number of requests. Below is an example of multiple Node instances.
const cluster = require('cluster') const os = require('os') const express = require('express')
// check if the process is the master process or child if(cluster.isMaster){ // finding the number of available cpu cores in the system const nCPUs = os.cpus().length; // fork worker processes for each available CPU core for(let i = 0; i< nCPUs; i++){ cluster.fork() } }else{ const app = express(); app.get("/getTimeStamp", (_req,res) => { res.send(Date.now().toString()); }) app.listen(3000, () => { console.log(`worker process ${process.pid} is listening on port 3000`); }); }
Cons of Multiprocessing
The performance boost of multiprocessing is costly. Implementing multiprocessing means running multiple Node instances in one application, and it will consume a lot of memory. Therefore you should always ensure that your server environment has sufficient memory. Multiprocessing, no matter which Node module you use, might require inter-process communication in certain applications, and it can get a little complex to maintain over a long time.
Conclusion of Multiprocessing:-
Multiprocessing can easily be implemented using Node Js built-in modules, and it allows you to make the best use of the available server environment. With multiprocessing, you can boost your server’s performance when executing CPU-intensive tasks and serving a larger number of requests responses, and improving server availability for all the requests.