Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use PM2 Cluster with Socket IO?

I am developing an application that relies completely on Socket.io. As we all know NodeJS by default runs only on one core. Now I would like to scale it across multiple cores. I am finding it difficult to make socketio work with PM2 Cluster Mode. Any sample code would help.

I am using Artillery to test. And when the app runs on single core I get the response while It runs in cluster the response would be NaN

When Ran in a Cluster

When Ran Without Cluster

like image 291
Bhuvanesh Arasu Avatar asked Oct 28 '22 17:10

Bhuvanesh Arasu


2 Answers

PM2 docs say

Be sure your application is stateless meaning that no local data is stored in the process, for example sessions/websocket connections, session-memory and related. Use Redis, Mongo or other databases to share states between processes.

Socket.io is not stateless.

Kubernetes implementation get around the statefull issues by routing based on source IP to a specific instance. This is still not 100% since some sources may present more than one IP address. I know this is not PM2, but gives you an idea of the complexity.

like image 126
grabbag Avatar answered Nov 08 '22 06:11

grabbag


NESTjs SERVER

I use Socket server 2.4.1 so then i get the compatible redis adapter that is 5.4.0

I need to extend nest's adepter class "ioAdapter" that class only works for normal ws connections not our pm2 clusters

import { IoAdapter } from '@nestjs/platform-socket.io';
import * as redisIOAdapter from 'socket.io-redis';
import { config } from './config';

export class RedisIoAdapter extends IoAdapter {
  createIOServer(port: number, options?: any): any {
    const server = super.createIOServer(port, options);
    const redisAdapter = redisIOAdapter({
      host: config.server.redisUrl,
      port: config.server.redisPort,
    });
    server.adapter(redisAdapter);
    return server;
  }
}

That is actually nestjs implementation

Now i need to tell nest im using that implementetion so i go to main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { config } from './config';
import { RedisIoAdapter } from './socket-io.adapter';
import { EventEmitter } from 'events';

async function bootstrap() {
  EventEmitter.defaultMaxListeners = 15;
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  app.useWebSocketAdapter(new RedisIoAdapter(app));
  await app.listen(config.server.port);
}
bootstrap();

I have a lot of events for this one so i had to up my max event count

now for every gateway you got, you need to use a different connection strategy, so instead of using polling you need to go to websocket directly

...
@WebSocketGateway({ transports: ['websocket'] })
export class AppGateway implements OnGatewayConnection, OnGatewayDisconnect {
...

or if you are using namespaces

...
@WebSocketGateway({ transports: ['websocket'], namespace: 'user' })
export class UsersGateway {
...

last step is to install the redis database on your AWS instance and that is another thing; and also install pm2

nest build
pm2 i -g pm2
pm2 start dist/main.js -i 4

CLIENT

const config: SocketIoConfig = {
  url: environment.server.admin_url, //http:localhost:3000
  options: {
    transports: ['websocket'],
  },
};

You can now test your websocket server using FireCamp

like image 21
Miguel Angel Romero Hernandez Avatar answered Nov 08 '22 05:11

Miguel Angel Romero Hernandez