Run a Node.js Application On Multiple CPU Cores Using Cluster
A single instance of a Node.js application runs on only one thread and, therefore, doesn't take full advantage of multi-core systems. There will be times when you may want to launch a cluster of Node.js processes to utilize each CPU core on a local machine or production server.
Luckily, Node.js has a core module named Cluster that helps us run a Node.js application using all the CPU cores on a machine.
In this article, we'll create a basic Node.js application and run an instance of it on each CPU core the host machine has. By doing so, the throughput of the Node.js application will increase along with each additional instance that is created.
Let's get started!
Table of Contents
Create a Node.js Application
Before anything else, we need to create a Node.js application to run. For demonstration purposes, let's create an application that runs a simple HTTP server.
Open a Node.js file and add this code to it:
const http = require("http")
const PORT = 5000
const server = http.createServer((req, res) => {
res.end("Hello there!")
})
server.listen(PORT, (err) => {
if (err) {
console.log(err)
} else {
console.log(`Server is listening on port ${PORT}`)
}
})
The first thing we do is import the HTTP core module. Since it's a Node.js core module, we don't need to install anything before we use it.
Then we define what port we want our basic HTTP server to run on and store it in the PORT
variable. In this case, we'll run the application on port 5000
.
Next, we use the http.createServer()
method to create an HTTP server and have it respond with "Hello there!" whenever it receives a request.
Last, we tell the HTTP server to listen on the 5000
port we specified with the server.listen()
method.
Awesome, now we have a basic Node.js application up and running. In the next step, we'll get it running on multiple CPU cores.
Run the Application on Multiple CPU Cores
Now we're ready to get this thing running on more than one CPU!
Open your Node.js file and update the code to this:
const http = require("http")
const os = require("os")
const cluster = require("cluster")
const PORT = 5000
const numOfCpuCores = os.cpus().length
if (numOfCpuCores > 1) {
if (cluster.isMaster) {
console.log(`Cluster master ${process.pid} is running.`)
for (let i=0; i < numOfCpuCores; i++) {
cluster.fork()
}
cluster.on("exit", function(worker) {
console.log("Worker", worker.id, " has exitted.")
})
} else {
const server = http.createServer((req, res) => {
res.end("Hello there!")
})
server.listen(PORT, (err) => {
if (err) {
console.log(err)
} else {
console.log(`Server is listening on port ${PORT} and process ${process.pid}.`)
}
})
}
} else {
const server = http.createServer((req, res) => {
res.end("Hello there!")
})
server.listen(PORT, (err) => {
if (err) {
console.log(err)
} else {
console.log(`Server is listening on port ${PORT} and process ${process.pid}.`)
}
})
}
A lot is going on in the code, so let's go through each part.
The first thing we do is require()
the express
npm package and the two Node.js core modules os
and cluster
.
Next, we create a PORT
variable and assign it a value of 5000
. We'll use this variable later on in our code.
Then we create a variable called numOfCpuCores
to represent the number of CPU cores the system has. We use the os.cpus().length
method to get that number.
Check out the Node.js documentation for more info on the os.cpus()
method.
Using the numOfCpuCores
value, we create an if...else
statement that checks whether or not the system has more than one CPU core. If the number of CPUs is greater than 1
, we move on with creating the cluster. But if there is only one CPU on the machine the code is running on, we start our Node.js application the same way we did in the first step of this tutorial on the single CPU core.
Assuming our machine has more than one CPU, the first thing we do is create another if...else
statement that checks whether or not this is the first process in the cluster that has run. We check that with the cluster.isMaster()
method that returns either true
or false
.
If that is the first process that has run, we use cluster.fork()
to spawn a new worker process for each of the CPU's that exist on the machine. And we also add an event listener that will log a message when a worker exits so we know when something goes wrong or unexpected.
The role of the master process is to listen to our HTTP server's port and load balance all of the requests among the workers.
Once all of our workers are spawned, we create a new instance of our Node.js application on each of the workers we created. For example, 2 instances of the application will be created if your machine has 2 CPU cores.
When you run the application, you should see this or something similar logged to the console:
Cluster master 30892 is running.
Server is listening on port 5000 and on process 30899.
Server is listening on port 5000 and on process 30904.
Server is listening on port 5000 and on process 30898.
Server is listening on port 5000 and on process 30911.
The output will vary depending on how many CPUs your system has.
You now have a Node.js application running on multiple CPUs!