From e0ccd2efb7c94c1929000c8c64eb3c83c0f4bdb2 Mon Sep 17 00:00:00 2001 From: Hamza Date: Thu, 16 Jul 2026 18:06:24 +0300 Subject: [PATCH] =?UTF-8?q?feat(realtime):=20Socket.IO=20Redis=20adapter?= =?UTF-8?q?=20=E2=80=94=20enable=20horizontal=20scaling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- backend/package.json | 1 + backend/src/main.ts | 6 ++++ backend/src/realtime/redis-io.adapter.ts | 46 ++++++++++++++++++++++++ docs/09-realtime.md | 2 +- 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 backend/src/realtime/redis-io.adapter.ts diff --git a/backend/package.json b/backend/package.json index a28d565..55a8983 100644 --- a/backend/package.json +++ b/backend/package.json @@ -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", diff --git a/backend/src/main.ts b/backend/src/main.ts index 9f77f29..997909c 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -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 }), diff --git a/backend/src/realtime/redis-io.adapter.ts b/backend/src/realtime/redis-io.adapter.ts new file mode 100644 index 0000000..b32cf04 --- /dev/null +++ b/backend/src/realtime/redis-io.adapter.ts @@ -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; + + constructor( + app: INestApplicationContext, + private readonly config: ConfigService, + ) { + super(app); + } + + async connectToRedis(): Promise { + const opts = { + host: this.config.get('redis.host'), + port: this.config.get('redis.port'), + db: this.config.get('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; + } +} diff --git a/docs/09-realtime.md b/docs/09-realtime.md index 20e05be..5efb0cd 100644 --- a/docs/09-realtime.md +++ b/docs/09-realtime.md @@ -2,7 +2,7 @@ ## التقنية - **Socket.IO** عبر `@nestjs/websockets` Gateway. -- **`socket.io-redis-adapter`** للتوسع الأفقي (عدة نسخ من الباك إند تتشارك الحالة). +- **`@socket.io/redis-adapter`** للتوسع الأفقي — **مفعّل** عبر `RedisIoAdapter` في `main.ts` (قنوات ببادئة `tripz:sio`). يسمح بتشغيل عدة نسخ تتشارك اتصالات الويب سوكت عبر Redis. - **Redis** للحضور (presence) والمطابقة الجغرافية (GEO commands). ## المصادقة على الاتصال