Scaling Node.js Applications

By Ararat Tonoyan
Picture of the author
Published on
scaling-nodejs-applications

Scaling Node.js Applications: From Humble Zero to Dashing Hero

Hello, dear web enthusiasts,

Let's talk about your Node.js application. It's got heart, it's got style, but does it have the might to carry your traffic aspirations? We're not talking "Spartan-in-training", we're talking "Hercules in his prime", here!

Today, we're delving into the art of scaling Node.js applications, and no, we don't mean the type of scaling you do in your graphic editor (though wouldn't it be wonderful if we could ctrl+'+' our way to performance?).

Let's unravel the mysteries of vertical and horizontal scalability, critique their approach to stress handling, and introduce a couple of powerful allies in this endeavour: the Cluster module and PM2.

Vertical vs Horizontal Scaling: The Clash of Titans

The world of scalability seems divided between Team Vertical and Team Horizontal. But fear not, it's less "West Side Story", and more "avocado toast vs. eggs Benedict".

Vertical Scaling is the "Just get a bigger server" strategy. You add more CPU, memory, storage and voila, you have a server that can handle more load. This is akin to taking a cute poodle and transforming it into a robust St. Bernard. But just like you can't turn a dog into an elephant, there's a hard limit to how much a server can be scaled up.

Horizontal Scaling is the "Make more servers" strategy. Instead of making one server powerful, you replicate it and distribute the load among the duplicates. It's like having a pack of poodles instead of one St. Bernard.

Now that we're well acquainted with the basics, let's embark on our journey with the Cluster module and PM2.

Node.js Scaling with the Cluster Module

The Cluster module allows you to create child processes (workers) that all run simultaneously, transforming your Node.js application into a multi-threaded powerhouse.

Here's an example of a simple server that spins up a worker for each CPU:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
  });
} else {
  // Workers can share any TCP connection
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello, world\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

But while this increases our processing power, the Cluster module is a bit like a knife in a gunfight when we're looking for serious scalability management.

Unleashing the Power of PM2

PM2 is your Node.js app's trusted sidekick, the Robin to your Batman if you will. This process manager handles load balancing, automatic restarts, zero-downtime reloads, and much more.

$ npm install pm2 -g

Now we're ready to start our application with PM2:

$ pm2 start app.js -i max

The -i max option here spins up as many instances as there are CPU

cores, handling horizontal scaling for us.

To Sum It Up

Vertically scaling your Node.js app can be useful in the short term, but you'll likely find the ceiling rather quickly. And unless you have a genie granting you unlimited server resources, the physical constraints of vertical scaling are a real party pooper.

On the other hand, horizontal scaling offers limitless possibilities, but comes with its own set of complexities: load balancing, data consistency, and communication among servers.

Using Node.js's Cluster module is a good step towards scalability, but for a robust, production-ready solution, PM2 takes the cake.

Now go forth, heroes of Node, and may your applications always withstand the onslaught of user requests!

Stay Tuned

Want to become a Solidity pro?
The best articles, links and news related to web development delivered once a week to your inbox.