0
0
ExpressHow-ToBeginner · 3 min read

How to Use Cluster with Express for Better Performance

Use the Node.js cluster module to create multiple worker processes that share the same Express server port. This allows your Express app to handle more requests by utilizing multiple CPU cores efficiently.
📐

Syntax

The cluster module creates a master process and multiple worker processes. The master listens for events and forks workers. Each worker runs an instance of your Express app.

  • cluster.isMaster: Checks if the current process is the master.
  • cluster.fork(): Creates a new worker process.
  • worker.process.pid: The process ID of the worker.
  • app.listen(port): Starts the Express server in each worker.
javascript
import cluster from 'cluster';
import os from 'os';
import express from 'express';

const numCPUs = os.cpus().length;

if (cluster.isMaster) {
  // Master process: fork workers
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.process.pid} died, forking a new one.`);
    cluster.fork();
  });
} else {
  // Worker process: run Express app
  const app = express();
  app.get('/', (req, res) => {
    res.send(`Hello from worker ${process.pid}`);
  });

  app.listen(3000, () => {
    console.log(`Worker ${process.pid} started`);
  });
}
💻

Example

This example shows how to use cluster with Express to run one worker per CPU core. Each worker listens on port 3000 and responds with its process ID. The master process manages workers and restarts any that exit unexpectedly.

javascript
import cluster from 'cluster';
import os from 'os';
import express from 'express';

const numCPUs = 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. Restarting...`);
    cluster.fork();
  });
} else {
  const app = express();

  app.get('/', (req, res) => {
    res.send(`Hello from worker ${process.pid}`);
  });

  app.listen(3000, () => {
    console.log(`Worker ${process.pid} started and listening on port 3000`);
  });
}
Output
Master 12345 is running Worker 12346 started and listening on port 3000 Worker 12347 started and listening on port 3000 ... (one line per CPU core)
⚠️

Common Pitfalls

  • Not handling worker exits: If a worker crashes and you don't restart it, your app loses capacity.
  • Sharing state between workers: Workers do not share memory, so global variables won't sync across them.
  • Listening on the same port: Only workers should listen on the port, not the master process.
  • Logging confusion: Logs from multiple workers can mix; consider adding worker IDs to logs.
javascript
/* Wrong: Master listens on port, workers do not */
import cluster from 'cluster';
import express from 'express';

if (cluster.isMaster) {
  const app = express();
  app.listen(3000); // Wrong: master should not listen
  cluster.fork();
} else {
  // Worker does listen
  const app = express();
  app.listen(3000);
}

/* Right: Only workers listen on port */
if (cluster.isMaster) {
  cluster.fork();
} else {
  const app = express();
  app.listen(3000);
}
📊

Quick Reference

  • Use cluster.isMaster to check master process.
  • Use cluster.fork() to create workers.
  • Workers run Express and listen on the same port.
  • Restart workers on exit to keep app stable.
  • Remember workers do not share memory.

Key Takeaways

Use Node.js cluster to run multiple Express workers on all CPU cores.
Only worker processes should listen on the server port, not the master.
Restart workers automatically when they exit to maintain availability.
Workers do not share memory; avoid relying on global state.
Add worker IDs to logs to track requests across processes.