In a express application, there will be port conflict if we start multiple processes.

Cluster module lets fork a process. You can fork multiple child process from a parent process. This way multiple process can acquire the same port

image.png

// Using cluster module to create child process ( Scaling NodsJS vertically )

import cluster from "cluster";
import os from "os";
import express from "express";

const app = express();
app.use(express.json());

// No of cores in your machine
const numOfCPUs = os.cpus().length;

// If primary core fork 
if(cluster.isPrimary){
    console.log(`Total CPUs are ${numOfCPUs}`)
    console.log(`Primary ${process.pid} is running`);
    for(let i=0;i<numOfCPUs;i++){
        cluster.fork();
    }

    // If a process dies fork again
    cluster.on("exit",(worker)=>{
        console.log(`Worker ${worker.process.pid} died`);
        console.log(`Let's fork another worker`)
        cluster.fork();
    })
}
else{
    console.log(`Worker ${process.pid} has started`);
    app.get("/",(req,res)=>{
        res.send("Hello world");
    })
	
		// CPU intensive task
    app.get("/api/:n",(req,res)=> {
        const num = parseInt(req.params.n);
        let count = 0;
        for(let i = 0; i<num;i++){
            count+=i;
        }

        res.send(`Final count is ${count} ${process.pid}`);
    })

    app.listen(3000,()=>{
        console.log("Listening on port 3000")
    })
}
// Sum all integers from 0 to 1000000000 in parallel, by dividing the work accross all CPU cores.

import cluster from "cluster";
import os from "os";

const numOfCPUs = os.cpus().length;
const target = 1000000000;
const chunkSize = Math.floor(target / numOfCPUs);

if(cluster.isPrimary){
    console.log(`Total Cores ${numOfCPUs}`);
    console.log(`Master process ${process.pid} started`);

    let totalSum = 0;
    let completedWorkers = 0;
    let startTime = Date.now();

   
    for(let i=0;i<numOfCPUs;i++){
        const worker = cluster.fork();

        let start = i * chunkSize;
        let end = ( i === numOfCPUs -1 ) ? target : ( i + 1 ) * chunkSize - 1 ;
        console.log(`Start: ${start} End: ${end}`);

         // Send message after listener is attached
        setTimeout(()=>{
            worker.send({ start, end });
        },100)

        // Message passing  ( Each worker sends back a message containing partial sum )
        worker.on("message",(msg) => {
            totalSum += msg.partialSum;
            completedWorkers++;

            if(completedWorkers === numOfCPUs){
            let endTime = Date.now();
            console.log(`Total Sum ${totalSum}`);
            console.log(`Time taken ${endTime - startTime}`);
            process.exit();
            }
        })  
    }
}
else{
    // This block run for each worker process
    process.on("message",(msg) => {
        console.log(`Worker ${process.pid} started`);
        const { start, end } = msg;
        let partialSum = 0;
        for(let i=start;i<=end;i++){
            partialSum+=i;
        }
        console.log(`Partial Sum ${partialSum}`);
        process.send({ partialSum })
    })
}