Real-Time SystemsAugust 04, 20267 min read

Real-Time Notifications and Event Streaming with WebSockets and Redis Pub/Sub

Nguyen Dai Long

Nguyen Dai Long

Backend Lead & Software Engineer

Users expect immediate feedback: whether it is team chat messages, workflow status updates, or system alerts, waiting for a manual page refresh or periodic polling is no longer acceptable. In this guide, I break down our architecture for delivering real-time notifications across thousands of concurrent clients using WebSockets and Redis Pub/Sub.

1. Polling vs. Long-Polling vs. WebSockets

HTTP polling inundates servers with empty requests (often 95%+ returning no new data), wasting bandwidth and exhausting database connections. Long-polling improves on this but still incurs repeated TCP/TLS handshake overhead. WebSockets provide a persistent, full-duplex TCP connection established once via an HTTP 101 Switching Protocols handshake, allowing instantaneous sub-10ms event delivery.

Key Implementation Takeaways:

  • WebSockets dramatically reduce network header overhead compared to repetitive polling.
  • Maintain bi-directional heartbeat (ping/pong) frames to detect dead connections promptly.
  • Authenticate the initial upgrade request securely using short-lived JWTs.

2. Scaling Across Container Nodes with Redis Pub/Sub

In a clustered production environment with multiple backend containers behind a load balancer, client A may be connected to Node 1, while the database event triggering a notification occurs on Node 2. To decouple our nodes, we utilized Redis Pub/Sub as a distributed message bus. When any service triggers an event, it publishes the payload to a designated Redis channel, and all listening nodes broadcast it to their local connected WebSocket subscribers.

pythonndlong.site
# Publishing an event to user channel
import json
import redis

r = redis.Redis(host='redis-cluster', port=6379, db=0)

def emit_user_notification(user_id: str, notification_type: str, message: str):
    channel = f"channel:user:{user_id}"
    payload = {
        "type": notification_type,
        "message": message,
        "timestamp": time.time()
    }
    r.publish(channel, json.dumps(payload))

Key Implementation Takeaways:

  • Redis Pub/Sub enables seamless horizontal scaling across stateless backend nodes.
  • Structure channels systematically (e.g., 'channel:user:{id}' or 'channel:team:{id}').
  • Keep published message payloads compact to minimize in-memory message broker overhead.

3. Client-Side Reliability: Heartbeats and Exponential Backoff

Network connections on mobile and laptop devices drop constantly when users switch Wi-Fi networks or sleep their devices. Our client-side WebSocket client implements automated exponential backoff with random jitter for reconnect attempts, alongside a missed-event sequence replay check upon reconnection.

Key Implementation Takeaways:

  • Never reconnect instantly in a loop—always use exponential backoff with jitter.
  • Track last-received message IDs to request missed updates during brief disconnects.
  • Provide clear UI connection status indicators to keep users informed.

Summary & Final Thoughts

WebSockets coupled with Redis Pub/Sub provide an elegant, scalable solution for interactive modern applications. With minimal operational complexity, you can deliver sub-second event streaming to thousands of active users reliably.

#WebSocket#Redis#Pub/Sub#Real-Time#Event-Driven
Nguyen Dai Long

Written by Nguyen Dai Long

Backend Engineer & Backend Lead with 4+ years of hands-on experience building production systems, RESTful APIs, and cloud infrastructure using Python (Django), Laravel, PostgreSQL, and Google Cloud Platform.