Multi-Threading with Clusters - Express.js Guide: The Comprehensive Book on Express.js (2014)

Express.js Guide: The Comprehensive Book on Express.js (2014)

Multi-Threading with Clusters

There are a lot of arguments out there against Node.js, which are rooted on the myth that Node.js-based systems have to be single-threaded. Nothing can be further from the truth — and with cluster module we can skillfully utilize all machines’ CPUs.

Here is a working example of an Express.js app that runs on four processes. At the beginning of the file, we import dependencies:

1 var cluster = require('cluster');

2 var http = require('http');

3 var numCPUs = require('os').cpus().length;

4 var express = require('express');

The cluster module has a property that tells us if the process is master or child. We use it to spawn four workers (the default workers will use the same file, but this can be overwritten with setupMaster). In addition to that, we can attach event listeners and receive messages from workers (e.g., ‘kill’).

1 if (cluster.isMaster) {

2 console.log (

3 ' Fork %s worker(s) from master',

4 numCPUs

5 )

6 for (var i = 0; i < numCPUs; i++) {

7 cluster.fork();

8 };

9 cluster.on('online', function(worker) {

10 console.log (

11 'worker is running on %s pid',

12 worker.process.pid

13 )

14 });

15 cluster.on('exit', function(worker, code, signal) {

16 console.log(

17 'worker with %s is closed',

18 worker.process.pid

19 );

20 });

21 }

The worker code is just an Express.js app with a twist — we’re getting the process PID:

1 } else if (cluster.isWorker) {

2 var port = 3000;

3 console.log(

4 'worker (%s) is now listening to http://localhost:%s',

5 cluster.worker.process.pid,

6 port

7 );

8 var app = express();

9 app.get('*', function(req, res) {

10 res.send(

11 200,

12 'cluser '

13 + cluster.worker.process.pid

14 + ' responded \n'

15 );

16 })

17 app.listen(port);

18 }

The full source code of expressjsguide/cluster.js:

1 var cluster = require('cluster');

2 var numCPUs = require('os').cpus().length;

3 var express = require('express');

4

5 if (cluster.isMaster) {

6 console.log (' Fork %s worker(s) from master', numCPUs);

7 for (var i = 0; i < numCPUs; i++) {

8 cluster.fork();

9 }

10 cluster.on('online', function(worker) {

11 console.log ('worker is running on %s pid', worker.process.pid);

12 });

13 cluster.on('exit', function(worker, code, signal) {

14 console.log('worker with %s is closed', worker.process.pid );

15 });

16 } else if (cluster.isWorker) {

17 var port = 3000;

18 console.log('worker (%s) is now listening to http://localhost:%s', cluster.work\

19 er.process.pid, port);

20 var app = express();

21 app.get('*', function(req, res) {

22 res.send(200, 'cluser ' + cluster.worker.process.pid + ' responded \n');

23 });

24 app.listen(port);

25 }

As usual, to start an app, run $ node cluster. There should be four (or two depending on your machine’s architecture) processes:

Starting of four processes with cluster.

When we CURL with $ curl http://localhost:3000, there are different processes that listen to the same port and respond to us:

Server response is rendered by different processes.

tip

Tip

If someone prefers ready solutions to low-level libraries (like cluster), check out the real-world production library created and used by eBay: cluster2 (GitHub).