Bhuvanesh Arasu
Bhuvanesh Arasu

Reputation: 71

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

Upvotes: 7

Views: 5076

Answers (4)

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

Upvotes: 2

MKT
MKT

Reputation: 46

You need to setup Redis with your Node server. Here is how I managed to get cluster mode to work with Socket.io

First install Redis. If you are using Ubuntu, follow this link: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04

Then:

npm i socket.io-redis

Now place Redis in your Node server

const redisAdapter = require('socket.io-redis')

global.io = require('socket.io')(server, { transports: [ 'websocket' ]})

io.adapter(redisAdapter({ host: 'localhost', port: 6379 }))

That was all I had to do to get PM2 cluster mode to work with socket.io in my server.

Upvotes: 0

Emmanuel Mahuni
Emmanuel Mahuni

Reputation: 1964

Try using this lib:

https://github.com/luoyjx/socket.io-redis-stateless

It makes socket io stateless through redis.

Upvotes: 0

grabbag
grabbag

Reputation: 1040

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.

Upvotes: 3

Related Questions