PM2 cluster mode with Node.js

Node.js has become very popular due to its support for microservices. It can handle many connections concurrently and offers scalability for your application. Node.js is a JavaScript runtime that allows you to execute client-side and server-side code using JavaScript.

Node.js runs in a single thread only, which handles all the requests in parallel. As a result, Node.js only utilizes a single CPU core, even if your server has multiple cores. To overcome this issue, a powerful tool called PM2 was introduced.

PM2 introduction

PM2 is an open-source, cross-platform process manager that helps keep your application alive at all times. It includes a built-in load balancer, which enables clustering without modifying Node.js code.

While there are other alternatives for process management, PM2 stands out with its numerous powerful features.

Here are some key features of PM2:

  • Automatic Restart: Whenever a Node.js application crashes or encounters an error, PM2 will automatically restart it.
  • Process Monitoring: PM2 keeps track of application health by storing logs, monitoring resource usage, and tracking the status of Node.js processes.
  • Load Balancing: PM2 simplifies load balancing with its built-in module.
  • Zero-Downtime Deployment: PM2 can reload your application, ensuring zero downtime during deployments.
  • Auto-start: PM2 saves the status of running processes and can auto-start them upon system reboot.
  • Multiple Application Management: With PM2, you can run different Node.js processes and optimize your server resources.

Now, let’s explore how to configure a cluster using simple examples:

Example 1: Cluster using the Node.js cluster module

Node.js has its own cluster module, which leverages system resources to improve application performance. It creates child processes that can be distributed across different cores.

Let’s create an app.js file using the example below:

const cluster = require('cluster');
const http = require('http');
const os = require('os');
const cpuLength = os.cpus().length;
if(cluster.isMaster) {
    // Fork as many workers as you have CPU cores
    for(let x=0; x<cpuLength; x++){ cluster.fork(); } cluster.on('exit', (worker, code, signal) => {
        console.log(`Worker ${worker.process.pid} died`);
    });
}
else{
    // spawn a HTTP server
    http.createServer((request, response) => {
        response.writeHead(200);
        response.end("hello world\n");
    }).listen(8080)
    console.log(`Worker ${process.pid} started`);
}

Next, run app.js using the following command.

nodejs app

Output:

Worker 9326 started
Worker 9334 started
Worker 9327 started
Worker 9333 started

In the above example, the master process creates a worker for each CPU core on the system. The system has four CPU cores, making four fork worker connections.

Cluster mode using PM2

PM2 is not installed with the Node package by default. You can install PM2 globally by executing the following command in the terminal.

npm install pm2 -g

Now, we can achieve the same cluster mode more effectively using PM2. Shrink the code above and copy-paste the following into app.js

app.js

var http = require('http');

http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
}).listen(8080);

Run the below command to achieve cluster mode using PM2.

sudo pm2 start app.js -i 4

Here, “app.js” is your application name, and “-i” represents the number of workers. Four workers are created using PM2, utilizing all four CPU cores.

Output:

Similarly, you can use the “max” option with PM2, which automatically detects the number of available CPUs and runs the maximum possible number of processes on the system.

sudo pm2 start app.js -i max

Or

sudo pm2 start app.js -i 0

If any individual process crashes, your application can still be served by the other running PM2 processes.
You can also scale up the cluster by using the following command

sudo pm2 scale app.js +1

The above command adds the workers to your existing running cluster. In the above example, one worker will be added.

PM2 load test

Let’s test the load by running the same application using Node.js and PM2.
For load testing, you need to install the loadtest module:

sudo npm install -g loadtest pm2

Furthermore, install the node express module using the following command:

sudo npm -g install express

Now, run the below order to generate an express application.

sudo express --view=pug exampleLoadtest

Next, go to the exampleLoadtest directory and replace the below code snippet in the app.js:

const express = require("express");
const app = express();
const port = 3000;

app.get("/", (request, response) => {
    response.send("Hello World!");
});

app.get("/load", (req, res) => {
    let total = 0;
    for (let i = 0; i < 7000000; i++) { 
        total++; 
    } 
    res.send(`The result of the CPU intensive task is ${total}\n`); 
}); 

app.listen(port, () => {
    console.log(`App listening on port ${port}`);
});

Now, run the application using node command:

sudo node app.js &

Then, execute the loadtest command to check the load:

sudo loadtest -n 1200 -c 200 -k http://localhost:3000/load

Output:

Requests: 0 (0%), requests per second: 0, mean latency: 0 ms
Requests: 575 (48%), requests per second: 116, mean latency: 1184.1 ms
Requests: 1156 (96%), requests per second: 116, mean latency: 1975.4 ms

Target URL: http://localhost:3000/load
Max requests: 1200
Concurrency level: 200
Agent: keepalive

Completed requests: 1200
Total errors: 0
Total time: 10.418248685 s
Requests per second: 115
Mean latency: 1587.4 ms

Percentage of the requests served within a certain time
50% 1710 ms
90% 1734 ms
95% 3937 ms
99% 3985 ms
100% 3989 ms (longest request)

Here, you can see the Total time is 10.418248685 s.

Let’s run the same app.js using PM2 and then do a load test to check the difference:

Kill the node process running in the background and then start using PM2.

sudo pm2 start app.js -i 4

Now execute the loadtest command:

sudo loadtest -n 1200 -c 200 -k http://localhost:3000/load

Output:

Requests: 0 (0%), requests per second: 0, mean latency: 0 ms

INFO Target URL: http://localhost:3000/load
Max requests: 1200
Concurrency level: 200
Agent: keepalive

Completed requests: 1200
Total errors: 0
Total time: 3.208657251 s
Requests per second: 374
Mean latency: 486.2 ms

Percentage of the requests served within a certain time
50% 185 ms
90% 2338 ms
95% 2533 ms
99% 2539 ms
100% 2608 ms (longest request)

Using PM2, the total time is 3.208657251 s.

By loadtest command, you can see the difference between running the same code using node.js and PM2 cluster.

PM2 status

You can list all the running processes using PM2 with the below commands:

sudo pm2 list

Or

sudo pm2 status

Zero downtime using PM2.
You can use the PM2 reload command to deploy a newer version or any changes you have made.

Syntax:

pm2 reload <app_name/all>

In our case:

pm2 reload app.js

PM2 will restart all the processes one by one so that you will achieve zero downtime in your production environment.

On the other hand, a PM2 restart will kill all the running processes and then restart all the processes simultaneously.

sudo pm2 kill

Monitoring and Logging:

PM2 also provides monitoring and logging features.

Monitoring system resources is straightforward; just run the below command in your terminal:

sudo pm2 monit

Output:

Keep the application running at system reboot.

You can generate and configure startup scripts by running the simple command below in the terminal.

sudo pm2 startup

Or

sudo pm2 startup systemd

Save the process list to start automatically at reboot

After executing the above command, you can save the currently running process list to start automatically at system reboot using the following command:

sudo pm2 save

To remove the script from autostart, just run the below command:

sudo pm2 unstartup systemd

You can check the logs by executing the following command:

sudo pm2 logs

The above command will show the logs of all the running processes by PM2.

To view the logs of the specific process, you can use the below command:

sudo pm2 logs app.js

It will show logs of app.js applications only.

You can clear all the PM2 logs by using the flush option:

sudo pm2 flush

You can also do logrotate, which can be very handy for the production server. To do so, you need to install pm2-logrotate.

sudo pm2 install pm2-logrotate

Conclusion

Using PM2, you can achieve clustering without putting in extra effort. You can easily scale your application resources, monitor multiple Node.js applications, achieve zero downtime, and much more than just clustering your application.
While there are other tools like supervisor and forever, PM2 has become the most popular tool among the Node.js community due to its flexibility and the features it offers with a minimal learning curve. Please feel free to ask any questions you may have.

About the Author: Sohan Patel

Share This Post, Choose Your Platform!

Request a Consultation