Satu server Node.js dapat menangani 500.000+ koneksi WebSocket idle dengan tuning yang tepat. Namun saat Anda menambahkan instansi server kedua, Anda menghadapi masalah penskalaan WebSocket yang mendasar: klien yang terhubung ke Server 1 tidak dapat menerima pesan dari Server 2 karena soket mereka hidup di proses yang berbeda. Redis Pub/Sub memecahkan ini dengan bertindak sebagai backplane pesan antara instansi server.
WebSocket bersifat stateful — tidak seperti permintaan HTTP, mereka mempertahankan koneksi persisten ke instansi server tertentu. Dalam deployment multi-server di belakang load balancer, klien terhubung ke tepat satu server. Ketika Anda ingin mengirim pesan ke pengguna yang mungkin terhubung ke server mana pun, Anda membutuhkan backplane yang mengkoordinasikan antar server — persis seperti yang diaktifkan Redis Pub/Sub.
Sticky session mengonfigurasi load balancer untuk merutekan klien yang sama ke server yang sama berdasarkan IP atau cookie mereka. Ini menghindari persyaratan backplane untuk komunikasi klien langsung, tetapi tidak memecahkan kasus di mana satu server perlu mengirim pesan ke klien di server lain. Sticky session juga rusak ketika instansi server di-restart — klien harus terhubung ulang dan mungkin mengenai server yang berbeda.
WebSocket Horizontal Scaling with Redis Pub/Sub
WITHOUT Redis Adapter (BROKEN):
─────────────────────────────────────────────
User A (connected to Server 1)
User B (connected to Server 2)
Server 1 emits notification to User B → DROPPED
Server 1 doesn't know B's socket is on Server 2!
WITH Redis Pub/Sub Adapter (CORRECT):
─────────────────────────────────────────────
┌────────────────────────────────────────┐
│ Load Balancer (Nginx + ip_hash) │
│ sticky sessions as safety net │
└──────────┬──────────────┬─────────────┘
│ │
┌──────────▼──┐ ┌────▼────────────┐
│ NestJS │ │ NestJS │
│ Gateway 1 │ │ Gateway 2 │
│ User A ✓ │ │ User B ✓ │
└──────────┬──┘ └────┬────────────┘
│ subscribe │
└──────┬────────┘
│
┌────────▼────────┐
│ Redis │
│ Pub/Sub │ ← Backplane
│ Channel: │
│ socket.io#room │
└─────────────────┘
When Server 1 needs to notify User B (room: userId):
Server 1 → publish to Redis → Server 2 subscribes → fan-out to User B's socket ✓Dari membangun fitur notifikasi real-time: gunakan Socket.IO rooms untuk mengelompokkan klien berdasarkan ID pengguna. Ketika Anda perlu mengirim notifikasi ke pengguna tertentu, emit ke room mereka (io.to(userId).emit(...)) daripada melacak ID soket. Adapter Redis memastikan keanggotaan room direplikasi di semua instansi server, sehingga emit ke room bekerja terlepas dari server mana soket pengguna berada.
Paket @socket.io/redis-adapter Socket.IO menggantikan adapter in-memory default dengan yang didukung Redis. Semua instansi server terhubung ke instansi Redis yang sama dan berlangganan ke saluran bersama. Ketika Server 1 emit ke room atau siaran, ia menerbitkan ke Redis. Semua server menerima publikasi dan fan-out ke klien yang terhubung secara lokal di room tersebut.
// main.ts — Redis adapter for Socket.IO
import { NestFactory } from '@nestjs/core';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const pubClient = createClient({ url: 'redis://redis:6379' });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
const io = app.get(Server); // get Socket.IO server instance
io.adapter(createAdapter(pubClient, subClient));
await app.listen(3000);
}
// notification.gateway.ts — NestJS WebSocket Gateway
@WebSocketGateway({ cors: { origin: '*' } })
export class NotificationGateway implements OnGatewayConnection {
@WebSocketServer() server: Server;
handleConnection(client: Socket) {
const userId = client.handshake.auth.userId;
// User joins their personal room — works across all server instances
client.join(userId);
}
// Called from any NestJS service (any server instance)
sendToUser(userId: string, event: string, data: unknown) {
// Redis adapter ensures this reaches the correct server
this.server.to(userId).emit(event, data);
}
// Called when invoice is approved — from the event handler
@OnEvent('invoice.approved')
async handleInvoiceApproved(payload: InvoiceApprovedEvent) {
this.sendToUser(payload.userId, 'notification', {
type: 'INVOICE_APPROVED',
invoiceId: payload.invoiceId,
message: 'Your invoice has been approved',
});
}
}Di NestJS, server WebSocket diimplementasikan sebagai Gateway — kelas yang didekorasi dengan @WebSocketGateway. Gateway menggunakan Socket.IO di bawah kapuk secara default. Untuk menambahkan adapter Redis, timpa hook afterInit di gateway Anda dan atur adapter secara programatik, atau konfigurasikan di tingkat aplikasi di main.ts.
Redis Pub/Sub bukan keajaiban. Ketika Anda mulai menyiarkan jutaan pesan per detik di 100K+ soket, Redis dapat menjadi bottleneck. Setiap pesan yang diterbitkan ke saluran dikirimkan ke setiap subscriber (semua instansi server Anda) — biaya fan-out per subscriber bertambah dengan jumlah server. Pada skala yang sangat tinggi, beralih ke Redis Cluster, gunakan NATS JetStream, atau Kafka untuk backplane.
Koneksi WebSocket terputus — pengguna mobile beralih antara WiFi dan seluler, tab browser tertidur, server di-restart. Bangun logika reconeksi ke dalam klien Anda. Pada reconeksi, server harus memutar ulang pesan yang terlewat. Implementasikan kursor 'last seen': ketika klien terhubung kembali, ia mengirim timestamp pesan terakhir yang diterima, dan server mengkueri notifikasi yang terlewat dari database.
Arsitektur deployment untuk server WebSocket yang diskalakan secara horizontal: load balancer (Nginx atau cloud LB) → beberapa instansi NestJS Gateway → backplane Redis Pub/Sub bersama. Konfigurasi load balancer untuk sticky session sebagai jaring pengaman. Jalankan Redis dalam konfigurasi High Availability (Sentinel atau Cluster) — jika Redis down, semua komunikasi WebSocket rusak.
Redis Pub/Sub bekerja dengan baik untuk skala sedang (puluhan ribu koneksi bersamaan, jutaan pesan per hari). Untuk skala yang lebih tinggi, pertimbangkan: NATS (sistem pesan yang dibuat khusus dengan overhead lebih rendah dari Redis untuk Pub/Sub), Kafka (untuk throughput sangat tinggi dengan riwayat pesan yang tahan lama), atau layanan WebSocket managed seperti Ably atau Pusher.