Lab 07: Cluster & IPC

Time: 60 minutes | Level: Architect | Docker: docker run -it --rm node:20-alpine sh

Node.js runs on a single CPU core by default. The cluster module forks multiple worker processes to utilize all cores. This lab covers cluster architecture, IPC messaging, sticky sessions, scheduling policies, and graceful restart patterns.


Step 1: Cluster Architecture

Master Process (cluster.isMaster)

├── Worker 1 (cluster.fork()) ─── IPC channel ──→ Master
├── Worker 2 (cluster.fork()) ─── IPC channel ──→ Master
├── Worker 3 (cluster.fork()) ─── IPC channel ──→ Master
└── Worker 4 (cluster.fork()) ─── IPC channel ──→ Master

OS-level: Workers share listening port via file descriptor passing
          Master handles ACCEPT for Round-Robin scheduling

Workers share the same port because the master holds the server socket and passes connections to workers via IPC.


Step 2: Basic Cluster Setup

// file: cluster-basic.js
const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {  // cluster.isMaster deprecated in Node 16+
  const numWorkers = Math.min(os.cpus().length, 4);
  console.log(`Master ${process.pid} starting ${numWorkers} workers`);

  for (let i = 0; i < numWorkers; i++) {
    const worker = cluster.fork();
    console.log(`  Forked worker ${worker.process.pid}`);
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died (code: ${code}, signal: ${signal})`);
    console.log('Forking replacement...');
    cluster.fork(); // auto-restart
  });

  cluster.on('online', (worker) => {
    console.log(`Worker ${worker.process.pid} is online`);
  });

} else {
  // Worker process
  const http = require('http');
  const server = http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Response from worker ${process.pid}\n`);
  });
  server.listen(8080, () => {
    console.log(`Worker ${process.pid} listening on :8080`);
  });
}

💡 Each worker is a full Node.js process with its own V8 heap and event loop. They share no memory — only communicate via IPC.


Step 3: IPC Messaging — Master ↔ Worker

📸 Verified Output (2-worker cluster):


Step 4: Sticky Sessions (Hash by IP)

In round-robin, subsequent requests from the same client may go to different workers (breaking session affinity). Sticky sessions route the same client to the same worker:

💡 For production sticky sessions, use a load balancer like nginx with ip_hash or HAProxy with stick-table. Node's cluster module is for single-machine scenarios.


Step 5: Scheduling Policies

💡 Round-Robin (SCHED_RR) is better than SCHED_NONE because OS scheduling can create hot spots where one worker gets all the connections.


Step 6: Graceful Worker Restart on SIGTERM


Step 7: Worker Health Monitoring


Step 8: Capstone — Production Cluster with Zero-Downtime Deploy


Summary

Concept
API
Description

Primary detection

cluster.isPrimary

Check if current process is master

Fork worker

cluster.fork()

Spawn a worker subprocess

Send to worker

worker.send(msg)

IPC from master to worker

Receive in worker

process.on('message', fn)

IPC from master to worker handler

Round-Robin

SCHED_RR

Evenly distribute connections

Sticky sessions

IP hash + socket passing

Route same client to same worker

Graceful shutdown

server.close() + exit

Drain connections before exit

Rolling restart

Fork new, then kill old

Zero-downtime deploy

Last updated