Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cluster mode and Socketio sticky sessions #14062

Open
5 of 15 tasks
Bubuioc opened this issue Oct 10, 2024 · 0 comments
Open
5 of 15 tasks

Cluster mode and Socketio sticky sessions #14062

Bubuioc opened this issue Oct 10, 2024 · 0 comments
Labels
needs triage This issue has not been looked into

Comments

@Bubuioc
Copy link

Bubuioc commented Oct 10, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

Please someone. How can i implement sticky session for a Nest app running in cluster mode.The Nest docs have no info about this and Socketio have no docs for nest(.I have posted a question on Socketio github discussions and Nest discord but nothing.I already implemented adapter but the sticky sessions from ** @socket.io/sticky** i can't setup.Maybe someone did this.

Minimum reproduction code

a

Steps to reproduce

No response

Expected behavior

This is main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';
import { Logger, ValidationPipe } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { SocketIOAdapter } from './core/socket/adapters/socket.io.adapter';
import * as cluster from 'cluster';
import * as os from 'os';
const clusterInstance = cluster as unknown as cluster.Cluster;
import { NODE_ENV } from './common/enums/node.env.enum';

async function bootstrap() {
  const logger = new Logger();
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
  app.enableCors({
    origin: '*',
  });

  const socketIOAdapter = new SocketIOAdapter(app);
  app.useWebSocketAdapter(socketIOAdapter);
  const configService: ConfigService = app.get(ConfigService);

  const port: number = configService.get('port');
  const nodeEnv: string = configService.get('NODE_ENV');

  if (nodeEnv !== NODE_ENV.PRODUCTION) {
    const config = new DocumentBuilder()
      .setTitle('Chat application')
      .setDescription('The corporative chat application written in Nest')
      .setVersion('1.0')
      .addBearerAuth({ type: 'http', name: 'accessToken' })
      .build();
    const document = SwaggerModule.createDocument(app, config);
    SwaggerModule.setup('api/docs', app, document);
  }

  await app.listen(port);

  logger.log(
    `Find docs on http://localhost:${port}/api/docs`,
    'NestApplication',
  );
}

const runningOnCluster: boolean =
  Object.values(NODE_ENV)
    .filter((env) => env !== NODE_ENV.LOCAL)
    .findIndex((env) => env === process.env.NODE_ENV) > -1;

if (runningOnCluster) {
  if (clusterInstance.isPrimary) {
    const cpuCount = os.cpus().length;
    for (let i = 0; i < cpuCount; i += 1) {
      clusterInstance.fork();
    }
    clusterInstance.on('online', (worker) => {
      Logger.log(`${worker.process.pid} is online`, 'NestApplication::Cluster');
    });
    clusterInstance.on('exit', ({ process }) => {
      Logger.log(`${process.pid} died`, 'NestApplication::Cluster');
    });
  } else {
    bootstrap();
  }
} else {
  bootstrap();
}

and this is redis streams adatper

import { INestApplicationContext } from '@nestjs/common';
import { IoAdapter } from '@nestjs/platform-socket.io';
import { Server, ServerOptions } from 'socket.io';
import { WsAuthMiddleware } from '../../../core/socket/middlewares/ws.auth.middleware';
import { createAdapter } from '@socket.io/redis-streams-adapter';
import { ConfigService } from '@nestjs/config';
import {
  DEFAULT_NAMESPACE,
  REDIS_CLIENT,
} from 'src/common/constants/constants';
import { WsQueryValidationMiddleware } from '../middlewares/ws.query.validation.middleware';
import { RedisClientType } from 'redis';

export class SocketIOAdapter extends IoAdapter {
  private configService: ConfigService;
  private redisClient: RedisClientType;
  private adapterConstructor: ReturnType<typeof createAdapter>;

  constructor(private app: INestApplicationContext) {
    super(app);
    this.configService = this.app.get(ConfigService);
    this.redisClient = this.app.get(REDIS_CLIENT);
    this._createClient();
  }

  private _createClient(): void {
    const streamName = this.configService.get<string>('redisConfig.streamName');
    this.adapterConstructor = createAdapter(this.redisClient, { streamName });
  }

  createIOServer(port: number, options?: ServerOptions): Server {
    const server: Server = super.createIOServer(port, {
      ...options,
      cors: {
        origin: (_req: any, callback: (arg0: null, arg1: boolean) => void) => {
          callback(null, true);
        },
        credentials: true,
      },
      transports: ['polling', 'websocket'],
      allowUpgrades: true,
    });
    server.adapter(this.adapterConstructor);

    server.of(DEFAULT_NAMESPACE).use(WsAuthMiddleware);
    server.of(DEFAULT_NAMESPACE).use(WsQueryValidationMiddleware);

    return server;
  }
}

Package

  • I don't know. Or some 3rd-party package
  • @nestjs/common
  • @nestjs/core
  • @nestjs/microservices
  • @nestjs/platform-express
  • @nestjs/platform-fastify
  • @nestjs/platform-socket.io
  • @nestjs/platform-ws
  • @nestjs/testing
  • @nestjs/websockets
  • Other (see below)

Other package

No response

NestJS version

No response

Packages versions

    "@nestjs/websockets": "^10.3.8",
    "@socket.io/redis-streams-adapter": "^0.2.2",
    "@socket.io/sticky": "^1.0.4",

Node.js version

No response

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

No response

@Bubuioc Bubuioc added the needs triage This issue has not been looked into label Oct 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs triage This issue has not been looked into
Projects
None yet
Development

No branches or pull requests

1 participant