DEV Community

Denny Danuwijaya
Denny Danuwijaya

Posted on

Clustering Nest.js

Server Clustering is a method of turning multiple computer servers into a cluster, which is a group of servers that acts like a single system. Its different with Load Balancer. Load Balancing is about the distribution of workloads across multiple computing resources, such as computers, server clusters, network links, etc.

In general, the cluster maximizes the processor performance of the server. If you have 8core processor, you can make it all work as cluster or you just need 2core, you can set it up.

This time, I will talk about how to create clusters for nestjs. As you know, Nestjs is a framework that can be relied on to build applications such as Rest API. I won't go into detail about the concept but will get straight to how it works.

Installation

For Nestjs Installation, you can look in the documentation. After you made it all, create app-cluster.service.ts in src/ directory.

import * as cluster from 'cluster';
import * as os from 'os';
import { Injectable } from '@nestjs/common';

const numCPUs = os.cpus().length;

@Injectable()
export class AppClusterService {
    static clusterize(callback: Function): void {
        if(cluster.isMaster){
            console.log(`Master server started on ${process.pid}`);
            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 {
            console.log(`Cluster server started on ${process.pid}`)
            callback();
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Then, you can called it in main.ts in src/ directory.


import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppClusterService } from './app_cluster.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}

//Call app-cluster.service.ts here.
AppClusterService.clusterize(bootstrap);
Enter fullscreen mode Exit fullscreen mode

Now, run you app using nodemon or pm2. I used PM2 to run the project. pm2 start dist/main.js -i max. -i max as a sign of how many cores we will use. If max, that means all we will use. Or you just change the max with specified number. (Note: run npm run build to generate dist directory.)

a. Using max resources.
image

b. Using specified resources. e.g 4
image

If either worker crashes or dies, it will be automatically moved to the live worker as the server prepares a new worker to replace the crashed worker.

Github Repo : https://github.com/danudenny/nestjs-cluster


I want to discuss further about this, maybe there is a statement from me that is wrong or maybe there is a suggestion to be more effective and efficient.

Top comments (4)

Collapse
 
tosiek88 profile image
Mateusz • Edited

Hi what about if you would like delegate only certain modules to separated thread? Let's imagine that I have Main thread , and some 2 modules with heavy endpoints and services, how to put them on same PORT in nest?

  const databaseService = nestApp.get<DatabaseService>(DatabaseService) // nest was breaking app when it was before routes
  const childProcess = nestApp.get<ChildProcessService>(ChildProcessService) // nest was breaking app when it was before routes
  childProcess.startNestBackgroundProcess({ nestHttpAdapter: nestApp.getHttpAdapter() })
Enter fullscreen mode Exit fullscreen mode
  startNestBackgroundProcess({ adapter }: any): void {
    if (!this.clusterModuleFork.send) {
      this.clusterModuleFork = fork(path.resolve(__dirname, './bootstrap'))
    }
    this.clusterModuleFork.send({ command: 'start', adapter })
  }
Enter fullscreen mode Exit fullscreen mode
async function bootstrap() {
  process.on('message', async ({ adapter }) => {
    const app = await NestFactory.create(ClusterizeModule, adapter)
    app.listen(8079) // I woul like to use sam port as in MainThread

    // process.send(cats)
  })
}
bootstrap()
Enter fullscreen mode Exit fullscreen mode

In Your cauterization will be possible to use same listen port ? 8080 Cheers nice article!

Collapse
 
chuanfengzhang profile image
Chuck • Edited

So the filename is app_cluster.service.ts instead of app-cluster.service.ts

Collapse
 
sangimed profile image
Mohamed I. • Edited

Hello,

Thanks for sharing.

I have doubts about that clusterize function and having to inject the bootstrap method as a callback because the PM2 cluster mode doesn't require ANY code modification.

The cluster mode allows networked Node.js applications (http(s)/tcp/udp server) to be scaled across all CPUs available, without any code modifications.

source : pm2.keymetrics.io/docs/usage/clust...

Collapse
 
rom858 profile image
Rom858

@Injectable in AppClusterService I think it doesnt need it because that marks a class as a provider. Providers can be injected into other classes via constructor parameter injection