Real-Time Messaging with Redis Pub/Sub
Pub/Sub lets one process publish a message to a channel and every subscribed process receive it instantly. A classic use is keeping WebSocket servers in sync.
The problem it solves
With three app instances behind a load balancer, user A's WebSocket might be on server 1 and user B's on server 3. When A sends B a message, server 1 must tell server 3. Redis Pub/Sub is that bridge.
Publishing and subscribing in Node.js
import { createClient } from 'redis';
const publisher = createClient({ url: process.env.REDIS_URL });
const subscriber = publisher.duplicate(); // a subscribed connection can't run other commands
await Promise.all([publisher.connect(), subscriber.connect()]);
// Every server subscribes
await subscriber.subscribe('chat:room:42', (raw) => {
const msg = JSON.parse(raw);
for (const socket of localSocketsInRoom('42')) socket.send(JSON.stringify(msg));
});
// Whichever server receives a message publishes it
async function onIncomingMessage(roomId, msg) {
await publisher.publish(`chat:room:${roomId}`, JSON.stringify(msg));
}
Pattern subscriptions
await subscriber.pSubscribe('notifications:user:*', (message, channel) => {
const userId = channel.split(':').at(-1);
deliverToUser(userId, message);
});
Know the limits
- Fire and forget: messages aren't stored. A subscriber that's disconnected (even briefly) misses them.
- At most once: no acknowledgements or retries.
- Every subscriber receives every message on its channels, so very high fan-out can stress Redis and the network.
For notifications that must not be lost, store them (in the database or a Redis Stream) and use Pub/Sub only as a "something changed, go check" signal. Libraries like Socket.IO have Redis adapters that implement this pattern for you.
Sharded Pub/Sub
In Redis Cluster, SSUBSCRIBE/SPUBLISH route each channel to one shard instead of broadcasting across the whole cluster, which scales much better.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.