Scalability is a hot topic in tech, and every programming language or framework provides its own way of handling high loads of traffic.
Today, we’re going to see an easy and straightforward example about Node.js clustering. This is a programming technique which will help you parallelize your code and speed up performance.
“A single instance of Node.js runs in a single thread. To take advantage of multi-core systems, the user will sometimes want to launch a cluster of Node.js processes to handle the load.”
- Node.js Documentation
We’re gonna create a simple web server using Koa, which is really similar to Express in terms of use.
The complete example is available in this Github repository.
What we’re gonna build

We’ll build a simple web server which will act as follows:
- Our server will receive a
POST
request, we’ll pretend that user is sending us a picture. - We’ll copy an image from the filesystem into a temporary directory.
- We’ll flip it vertically using Jimp, an image processing library for Node.js.
- We’ll save it to the file system.
- We’ll delete it and we’ll send a response to the user.
Of course, this is not a real world application, but is pretty close to one. We just want to measure the benefits of using clustering.
Setting up the project
I’m gonna use
yarn
to install my dependencies and initialize my project:
$ yarn init
$ yarn add -D forever
$ yarn add jimp koa koa-router
Since Node.js is single threaded, if our web server crashes, it will remain down until some other process will restarts it. So we’re gonna install forever, a simple daemon which will restart our web server if it ever crashes.
We’ll also install Jimp, Koa and Koa Router.
Getting started with Koa
This is the folder structure we need to create:

We’ll have an
src
folder which contains two JavaScript files: cluster.js
and standard.js
.
The first one will be the file where we’ll experiment with the
cluster
module. The second is a simple Koa server which will work without any clustering.
In the
module
directory, we’re gonna create two files: job.js
and log.js
.job.js
will perform the image manipulation work. log.js
will log every event that occurs during that process.The Log module
Log module will be a simple function which will take an argument and will write it to the
stdout
(similar to console.log
).
It will also append the current timestamp at the beginning of the log. This will allow us to check when a process started and to measure its performance.
module.exports = function log(args) {
return process.stdout.write(`[${+ new Date()}] - ${args}\n`)
}
node-log.js
The Job module
I’ll be honest, this is not a beautiful and super-optimized script. It’s just an easy job which will allow us to stress our machine.
const fs = require('fs')
const jimp = require('jimp')
const log = require('./log')
module.exports = function runJob() {
return new Promise(async (resolve, reject) => {
const randomNumber = () => Math.floor(Math.random() * 1995) * 15
const destFileName = `${__dirname}/../imgs/dest/${randomNumber()}-img.jpg`
log(`Copying ${destFileName}`)
fs.copyFileSync(`${__dirname}/../imgs/landscape.jpg`, destFileName)
log(`Flipping ${destFileName}`)
const image = await jimp.read(destFileName)
image.flip(true, false)
log(`Deleting ${destFileName}`)
fs.unlink(destFileName, (err) => {
return err ? reject(err) : resolve('success')
})
})
}
node-job.js
The Koa Webserver
We’re gonna create a very simple webserver. It will respond on two routes with two different HTTP methods.
We’ll be able to perform a GET request on
http://localhost:3000/
. Koa will respond with a simple text which will show us the current PID (process id).
The second route will only accept POST requests on the
/flip
path, and will perform the job that we just created.
We’ll also create a simple middleware which will set an
X-Response-Time
header. This will allow us to measure the performance.
const Koa = require('koa')
const Router = require('koa-router')
const runJob = require('./modules/job')
const log = require('./modules/log')
const router = new Router()
const app = new Koa()
router.get('/', async ctx => ctx.body = `PID ${process.pid} listening here!`)
.post('/flip', async ctx => {
const res = await runJob()
ctx.body = res
})
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.get('X-Response-Time');
log(`${ctx.method} ${ctx.url} - ${rt}`);
})
.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
})
.use(router.routes())
.listen(3000)
koa-standard.js
The Node.js server will immediately copy our image and start to flip it.
The first response will be logged after 16 seconds and the last one after 40 seconds.
The first response will be logged after 16 seconds and the last one after 40 seconds.
Such a dramatic performance decrease! With just 10 concurrent requests, we decreased the webserver performance by 950%!
Introducing clustering
:
emember what I mentioned at the beginning of the article?
To take advantage of multi-core systems, the user will sometimes want to launch a cluster of Node.js processes to handle the load.
Depending on which server we’re gonna run our Koa application, we could have a different number of cores.
Every core will be responsible for handling the load individually. Basically, each HTTP request will be satisfied by a single core.
So for example — my machine, which has eight cores, will handle eight concurrent requests.
We can now count how many CPUs we have thanks to the
os
module:const { cpus } = require('os')
const numWorkers = cpus().length
The
cpus() method will return an array of objects that describe our CPUs. We can bind its length to a constant which will be called numWorkers , ’cause that’s the number of workers that we’re gonna use.
We’re now ready to require the
cluster module. | |
Comments
Post a Comment