Bird
Raised Fist0
Node.jsframework~10 mins

Load balancing between workers in Node.js - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Concept Flow - Load balancing between workers
Start Master Process
Fork Worker Processes
Workers Listen for Requests
OS Distributes to Workers
Workers Process Requests
Send Response Back
OS Balances Load
More Requests?
No
Shutdown
The master process forks workers. Workers listen for requests on the same port, and the OS distributes them evenly to workers for processing.
Execution Sample
Node.js
import cluster from 'node:cluster';
import { cpus } from 'node:os';
import http from 'node:http';

if (cluster.isPrimary) {
  for (let i = 0; i < cpus().length; i++) cluster.fork();
} else {
  http.createServer((req, res) => {
    res.end(`Handled by worker ${process.pid}`);
  }).listen(8000);
}
This code forks one worker per CPU core and balances incoming HTTP requests among them.
Execution Table
StepActionMaster StateWorker StateLoad Distribution
1Master starts and checks cluster.isPrimaryPrimary: true, forks 4 workersNo workers yetNo load yet
2Master forks worker 1Primary: true, 1 worker forkedWorker 1 startedNo load yet
3Master forks worker 2Primary: true, 2 workers forkedWorker 2 startedNo load yet
4Master forks worker 3Primary: true, 3 workers forkedWorker 3 startedNo load yet
5Master forks worker 4Primary: true, 4 workers forkedWorker 4 startedNo load yet
6Workers listen on port 8000Primary: trueWorkers listeningNo load yet
7Request 1 arrivesPrimary: trueWorker 1 handles requestRequest 1 -> Worker 1
8Request 2 arrivesPrimary: trueWorker 2 handles requestRequest 2 -> Worker 2
9Request 3 arrivesPrimary: trueWorker 3 handles requestRequest 3 -> Worker 3
10Request 4 arrivesPrimary: trueWorker 4 handles requestRequest 4 -> Worker 4
11Request 5 arrivesPrimary: trueWorker 1 handles requestRequest 5 -> Worker 1
12No more requestsPrimary: trueWorkers idleLoad balanced evenly
13Master shuts down workersPrimary: true, workers shutdownWorkers stoppedNo load
💡 No more requests; all workers have handled requests evenly; master shuts down.
Variable Tracker
VariableStartAfter 1After 2After 3After 4After 5Final
workersForked0123444
requestsHandledByWorker10111122
requestsHandledByWorker20011111
requestsHandledByWorker30001111
requestsHandledByWorker40000111
Key Moments - 3 Insights
Why does the master fork multiple workers instead of handling requests itself?
The master process forks workers to use multiple CPU cores for parallel processing. The master does not handle requests directly; workers do, as shown in steps 1-5 and 7-11 in the execution table.
How does the master decide which worker gets a request?
Node.js cluster module uses a round-robin approach by default on most platforms, distributing requests evenly among workers. This is seen in the execution table where requests 1 to 5 are assigned to workers 1 to 4, then back to worker 1.
What happens if a worker crashes?
The master can detect worker exit events and fork a new worker to replace it, maintaining load balance. This is not shown in the sample but is part of cluster management best practices.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, which worker handles the 3rd request?
AWorker 3
BWorker 2
CWorker 1
DWorker 4
💡 Hint
Check row 9 in the execution table where Request 3 is handled.
At which step does the master finish forking all workers?
AStep 4
BStep 5
CStep 6
DStep 7
💡 Hint
Look at the 'Master forks worker 4' action in the execution table.
If the number of CPUs doubles, how would the 'workersForked' variable change after forking?
AIt stays the same
BIt halves
CIt doubles
DIt becomes zero
💡 Hint
Refer to the variable_tracker row for 'workersForked' and the for loop in the code sample.
Concept Snapshot
Load balancing in Node.js cluster:
- Master process forks workers equal to CPU cores
- Workers listen on the same port
- Requests are balanced round-robin by default (OS/kernel level)
- Workers handle requests independently
- Master can restart crashed workers to maintain balance
Full Transcript
In Node.js, load balancing between workers is done using the cluster module. The master process starts and forks one worker per CPU core. Each worker runs its own server instance. Incoming requests are distributed evenly among workers, usually in a round-robin fashion by the OS. Workers process requests independently and send responses back. This approach uses all CPU cores efficiently. The master can monitor workers and restart any that crash to keep the system balanced. The execution table shows the master forking workers, workers starting servers, and requests being assigned one by one to each worker in turn. Variables track how many workers are forked and how many requests each worker handles. This method improves performance by parallelizing work across CPU cores.

Practice

(1/5)
1. What is the main purpose of using the cluster module in Node.js for load balancing?
easy
A. To spread incoming requests across multiple CPU cores using worker processes
B. To create a single-threaded server that handles all requests
C. To manage database connections efficiently
D. To optimize memory usage by compressing data

Solution

  1. Step 1: Understand the role of the cluster module

    The cluster module allows Node.js to create multiple worker processes that share the same server port.
  2. Step 2: Identify the purpose of load balancing

    Load balancing means distributing incoming requests evenly across workers to use CPU cores efficiently.
  3. Final Answer:

    To spread incoming requests across multiple CPU cores using worker processes -> Option A
  4. Quick Check:

    Load balancing = spreading requests across workers [OK]
Hint: Cluster module creates workers to share server load [OK]
Common Mistakes:
  • Thinking cluster creates a single-threaded server
  • Confusing load balancing with database management
  • Assuming cluster compresses data for memory optimization
2. Which of the following is the correct way to check if the current process is the master in a Node.js cluster?
easy
A. if (cluster.isWorker) { /* master code */ }
B. if (cluster.master) { /* master code */ }
C. if (cluster.isMaster) { /* master code */ }
D. if (cluster.worker) { /* master code */ }

Solution

  1. Step 1: Recall the cluster API properties

    Node.js cluster module provides isMaster and isWorker boolean properties to identify process roles.
  2. Step 2: Identify the correct property for master check

    cluster.isMaster is true if the process is the master, so the condition should use this.
  3. Final Answer:

    if (cluster.isMaster) { /* master code */ } -> Option C
  4. Quick Check:

    Master check uses cluster.isMaster [OK]
Hint: Use cluster.isMaster to detect master process [OK]
Common Mistakes:
  • Using cluster.isWorker to check for master
  • Using non-existent properties like cluster.master
  • Confusing cluster.worker with cluster.isWorker
3. Consider this Node.js cluster code snippet:
const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {
  cluster.fork();
  cluster.fork();
} else {
  http.createServer((req, res) => {
    res.end(`Worker ${process.pid} handled request`);
  }).listen(8000);
}
What will happen when you send multiple requests to port 8000?
medium
A. Requests will be handled by both worker processes, distributing load
B. Only the first worker will handle all requests, the second is idle
C. The master process will handle requests directly
D. The server will crash because multiple workers listen on the same port

Solution

  1. Step 1: Understand cluster.fork() behavior

    Calling cluster.fork() creates worker processes that share the same server port.
  2. Step 2: Analyze request handling in workers

    Both workers listen on port 8000 and Node.js load balances requests between them automatically.
  3. Final Answer:

    Requests will be handled by both worker processes, distributing load -> Option A
  4. Quick Check:

    Multiple workers share port and balance requests [OK]
Hint: Multiple forks share port and balance requests [OK]
Common Mistakes:
  • Thinking only one worker handles all requests
  • Assuming master handles requests directly
  • Believing server crashes due to port sharing
4. Given this cluster code snippet, what is the main issue causing the worker to never restart after crashing?
const cluster = require('cluster');

if (cluster.isMaster) {
  cluster.fork();
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
  });
} else {
  throw new Error('Crash');
}
medium
A. The exit event listener is attached to the wrong object
B. The worker code does not handle errors properly
C. The cluster module is not imported correctly
D. The master does not fork a new worker after exit event

Solution

  1. Step 1: Check the exit event handler

    The master listens for 'exit' but only logs the death, it does not fork a new worker.
  2. Step 2: Identify missing restart logic

    To restart workers after crash, the master must call cluster.fork() inside the exit event handler.
  3. Final Answer:

    The master does not fork a new worker after exit event -> Option D
  4. Quick Check:

    Restart requires forking new worker on exit [OK]
Hint: Fork new worker inside exit event to restart [OK]
Common Mistakes:
  • Assuming worker error handling restarts process
  • Thinking cluster import affects restart
  • Believing exit event is attached incorrectly
5. You want to implement a Node.js cluster that balances load across all CPU cores and automatically restarts workers if they crash. Which code snippet correctly achieves this?
hard
A. if (cluster.isMaster) { cluster.fork(); cluster.on('exit', () => { console.log('Worker died'); }); } else { require('http').createServer((req, res) => { res.end('Hello'); }).listen(3000); }
B. if (cluster.isMaster) { const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting...`); cluster.fork(); }); } else { require('http').createServer((req, res) => { res.end(`Handled by worker ${process.pid}`); }).listen(3000); }
C. if (cluster.isWorker) { const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } } else { require('http').createServer((req, res) => { res.end('Worker running'); }).listen(3000); }
D. if (cluster.isMaster) { const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } } else { require('http').createServer((req, res) => { res.end('Worker running'); }).listen(3000); cluster.on('exit', () => { cluster.fork(); }); }

Solution

  1. Step 1: Fork workers equal to CPU cores in master

    if (cluster.isMaster) { const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting...`); cluster.fork(); }); } else { require('http').createServer((req, res) => { res.end(`Handled by worker ${process.pid}`); }).listen(3000); } correctly uses os.cpus().length to fork that many workers in the master process.
  2. Step 2: Restart workers on exit event in master

    if (cluster.isMaster) { const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting...`); cluster.fork(); }); } else { require('http').createServer((req, res) => { res.end(`Handled by worker ${process.pid}`); }).listen(3000); } listens to the 'exit' event on cluster and forks a new worker to replace the dead one.
  3. Step 3: Worker creates HTTP server listening on port 3000

    if (cluster.isMaster) { const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting...`); cluster.fork(); }); } else { require('http').createServer((req, res) => { res.end(`Handled by worker ${process.pid}`); }).listen(3000); }'s else block creates the server in workers, which is correct for load balancing.
  4. Final Answer:

    Forks workers across all CPU cores and automatically restarts workers if they crash -> Option B
  5. Quick Check:

    Fork all CPUs + restart on exit = if (cluster.isMaster) { const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting...`); cluster.fork(); }); } else { require('http').createServer((req, res) => { res.end(`Handled by worker ${process.pid}`); }).listen(3000); } [OK]
Hint: Fork all CPUs in master and restart on exit event [OK]
Common Mistakes:
  • Not forking all CPU cores
  • Not restarting workers after crash
  • Forking workers inside worker process
  • Attaching exit listener inside worker instead of master