feat(realtime): Socket.IO Redis adapter — enable horizontal scaling
- RedisIoAdapter wires @socket.io/redis-adapter with dedicated pub/sub clients - channel key 'tripz:sio' isolates on shared Redis - wired in main.ts via useWebSocketAdapter; multiple app replicas can now share WS connections Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
"@nestjs/throttler": "^6.2.0",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"@nestjs/websockets": "^11.0.0",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"bullmq": "^5.12.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
|
||||
@@ -4,11 +4,17 @@ import { ValidationPipe, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
import { RedisIoAdapter } from './realtime/redis-io.adapter';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
const cfg = app.get(ConfigService);
|
||||
|
||||
// مُحوّل Socket.IO عبر Redis — يفعّل التوسّع الأفقي (عدة نسخ)
|
||||
const redisIoAdapter = new RedisIoAdapter(app, cfg);
|
||||
await redisIoAdapter.connectToRedis();
|
||||
app.useWebSocketAdapter(redisIoAdapter);
|
||||
|
||||
app.setGlobalPrefix('api');
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({ whitelist: true, transform: true }),
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { INestApplicationContext, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { IoAdapter } from '@nestjs/platform-socket.io';
|
||||
import { ServerOptions } from 'socket.io';
|
||||
import { createAdapter } from '@socket.io/redis-adapter';
|
||||
import Redis from 'ioredis';
|
||||
|
||||
/**
|
||||
* مُحوّل Socket.IO مدعوم بـ Redis — يجعل التطبيق قابلاً للتشغيل بعدة نسخ
|
||||
* (توسّع أفقي) تتشارك اتصالات الويب سوكت عبر Redis. بلا هذا، رسالة من نسخة
|
||||
* لا تصل زبوناً متصلاً بنسخة أخرى (راجع docs/09).
|
||||
* مفتاح القنوات ببادئة tripz للعزل عن أي تطبيق آخر على نفس Redis.
|
||||
*/
|
||||
export class RedisIoAdapter extends IoAdapter {
|
||||
private readonly logger = new Logger('SocketRedis');
|
||||
private adapterConstructor: ReturnType<typeof createAdapter>;
|
||||
|
||||
constructor(
|
||||
app: INestApplicationContext,
|
||||
private readonly config: ConfigService,
|
||||
) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
async connectToRedis(): Promise<void> {
|
||||
const opts = {
|
||||
host: this.config.get<string>('redis.host'),
|
||||
port: this.config.get<number>('redis.port'),
|
||||
db: this.config.get<number>('redis.db'),
|
||||
maxRetriesPerRequest: null,
|
||||
};
|
||||
const pub = new Redis(opts);
|
||||
const sub = pub.duplicate();
|
||||
pub.on('error', (e) => this.logger.warn(`pub: ${e.message}`));
|
||||
sub.on('error', (e) => this.logger.warn(`sub: ${e.message}`));
|
||||
// key يعزل قنوات socket.io الخاصة بـ Tripz عن غيرها على نفس Redis
|
||||
this.adapterConstructor = createAdapter(pub, sub, { key: 'tripz:sio' });
|
||||
this.logger.log('Socket.IO Redis adapter ready (horizontal scale enabled)');
|
||||
}
|
||||
|
||||
createIOServer(port: number, options?: ServerOptions): any {
|
||||
const server = super.createIOServer(port, options);
|
||||
if (this.adapterConstructor) server.adapter(this.adapterConstructor);
|
||||
return server;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user