# Scaling WebSockets and Server-Sent Events — Redis Pub/Sub & Streams

Source: https://www.skillbyai.com/en/redis-streams/a-realtime

> Fan out live updates across many WebSocket servers with Redis.

## A backplane for real-time servers

A single WebSocket or SSE server can push messages only to users connected to **it**. With many servers behind a load balancer, a message for user 42 must reach whichever server holds user 42's connection. Redis is a common **backplane**: every real-time server subscribes to relevant channels (per user, per room or per topic), and any service publishes updates to Redis, which fans them out to all servers; each server forwards to its own connected clients. Frameworks build on this: the **Socket.IO Redis adapter** (including a sharded variant for Redis 7), **SignalR's Redis backplane** in ASP.NET Core, and **Spring** with Redis message listeners. Because Pub/Sub is fire-and-forget, design clients to **resynchronise** after reconnecting, for example by fetching missed notifications from an API, or by reading recent entries from a Redis Stream that also stores the updates.

## A Node.js WebSocket server using Redis as a backplane

Each server subscribes once and forwards messages to its local sockets for that user.

```js
import { createClient } from 'redis';
import { WebSocketServer } from 'ws';

const sub = createClient({ url: process.env.REDIS_URL });
await sub.connect();

const wss = new WebSocketServer({ port: 8080 });
const socketsByUser = new Map();           // userId -> Set of sockets on THIS server

wss.on('connection', (ws, req) => {
  const userId = authenticate(req);        // e.g. from a token
  if (!socketsByUser.has(userId)) socketsByUser.set(userId, new Set());
  socketsByUser.get(userId).add(ws);
  ws.on('close', () => socketsByUser.get(userId)?.delete(ws));
});

await sub.pSubscribe('notify:user:*', (message, channel) => {
  const userId = channel.split(':')[2];
  for (const ws of socketsByUser.get(userId) ?? []) ws.send(message);
});

// any service: await redis.publish(`notify:user:${userId}`, JSON.stringify(payload));
```

## Switchboard operators in different buildings

Each building has an operator connected to its own phones. A central announcement system (Redis) tells every operator about a call for extension 42; only the operator who has extension 42 actually rings it.

**Quiz:** Why do multiple WebSocket servers need a backplane such as Redis Pub/Sub?

- [ ] WebSockets cannot send JSON
- [ ] Redis encrypts WebSocket traffic
- [x] A message must reach whichever server holds the target user's connection
- [ ] Load balancers cannot route WebSockets

*Answer:* A message must reach whichever server holds the target user's connection. The backplane fans messages out so the server holding the right connection can deliver them.
