ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

WebSocket Architecture

Build real-time applications with WebSocket connections. Covers connection lifecycle, scaling WebSockets, heartbeat management, reconnection strategies, message protocols, and the patterns that enable real-time features like chat, notifications, and live updates.

WebSockets enable real-time, bidirectional communication between client and server. Unlike HTTP request-response where the client must poll for updates, WebSockets let the server push data to the client instantly. This powers chat, notifications, live dashboards, collaborative editing, and real-time gaming.


HTTP vs WebSocket

HTTP (Request-Response):
  Client: "Any new messages?"        → Server: "No"
  Client: "Any new messages?"        → Server: "No"
  Client: "Any new messages?"        → Server: "Yes, 1 message"
  
  Polling interval: 1 second
  99% of requests return nothing
  High bandwidth waste, latency = polling interval

WebSocket (Bidirectional):
  Client: "Open connection"          → Server: "Connected ✓"
  Server: "New message!"             → Client: (instant)
  Server: "User typing..."           → Client: (instant)
  Client: "Send: Hello"              → Server: (instant)
  
  Single long-lived connection
  Server pushes data immediately
  Zero wasted requests, near-zero latency

Server Implementation

# FastAPI WebSocket server
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import asyncio
import json

app = FastAPI()

class ConnectionManager:
    """Manage active WebSocket connections."""
    
    def __init__(self):
        self.connections: dict[str, list[WebSocket]] = {}  # room -> connections
    
    async def connect(self, websocket: WebSocket, room: str, user_id: str):
        await websocket.accept()
        
        if room not in self.connections:
            self.connections[room] = []
        self.connections[room].append(websocket)
        
        # Notify room
        await self.broadcast(room, {
            "type": "user_joined",
            "user_id": user_id,
            "timestamp": datetime.utcnow().isoformat(),
        })
    
    async def disconnect(self, websocket: WebSocket, room: str):
        self.connections[room].remove(websocket)
    
    async def broadcast(self, room: str, message: dict):
        """Send message to all connections in a room."""
        if room in self.connections:
            dead_connections = []
            for connection in self.connections[room]:
                try:
                    await connection.send_json(message)
                except Exception:
                    dead_connections.append(connection)
            
            # Clean up dead connections
            for conn in dead_connections:
                self.connections[room].remove(conn)

manager = ConnectionManager()

@app.websocket("/ws/{room_id}")
async def websocket_endpoint(websocket: WebSocket, room_id: str):
    user_id = websocket.query_params.get("user_id", "anonymous")
    await manager.connect(websocket, room_id, user_id)
    
    try:
        while True:
            data = await websocket.receive_json()
            
            # Handle different message types
            if data["type"] == "message":
                await manager.broadcast(room_id, {
                    "type": "message",
                    "user_id": user_id,
                    "content": data["content"],
                    "timestamp": datetime.utcnow().isoformat(),
                })
            
            elif data["type"] == "typing":
                await manager.broadcast(room_id, {
                    "type": "typing",
                    "user_id": user_id,
                })
    
    except WebSocketDisconnect:
        await manager.disconnect(websocket, room_id)

Scaling WebSockets

Single Server:
  Server holds all connections in memory
  Works for < 10,000 connections
  Problem: What if user A is on Server 1, user B on Server 2?
  
Multi-Server with Pub/Sub:
  ┌──────────┐    ┌──────────┐    ┌──────────┐
  │ Server 1 │    │ Server 2 │    │ Server 3 │
  │ 5K conns │    │ 5K conns │    │ 5K conns │
  └────┬─────┘    └────┬─────┘    └────┬─────┘
       │               │               │
       └───────────────┼───────────────┘

               ┌───────┴───────┐
               │  Redis Pub/Sub │
               │  (message bus) │
               └───────────────┘
  
  Server 1 receives message from User A
  Publishes to Redis channel "room:123"
  Server 2 and Server 3 receive from Redis
  Forward to their connected users in room:123

Client Reconnection

class WebSocketClient {
  constructor(url) {
    this.url = url;
    this.reconnectDelay = 1000;
    this.maxDelay = 30000;
    this.connect();
  }
  
  connect() {
    this.ws = new WebSocket(this.url);
    
    this.ws.onopen = () => {
      console.log('Connected');
      this.reconnectDelay = 1000;  // Reset on success
    };
    
    this.ws.onclose = (event) => {
      if (!event.wasClean) {
        // Exponential backoff with jitter
        const jitter = Math.random() * 1000;
        const delay = Math.min(this.reconnectDelay + jitter, this.maxDelay);
        
        console.log(`Reconnecting in ${delay}ms...`);
        setTimeout(() => this.connect(), delay);
        
        this.reconnectDelay *= 2;  // Double delay each attempt
      }
    };
    
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.handleMessage(data);
    };
  }
}

Anti-Patterns

Anti-PatternConsequenceFix
No heartbeat/pingDead connections kept alivePeriodic ping/pong (every 30 seconds)
No reconnection logicOne disconnect = permanent lossExponential backoff with jitter
Store state only in WS memoryLost on server restartPersist to Redis/database
No authenticationAnyone can connectToken-based auth on connection
Single server, no pub/subCannot scale horizontallyRedis pub/sub for multi-server

WebSockets are the right tool for real-time features. But they require more operational care than stateless HTTP — connection management, scaling, and reconnection must all be handled explicitly.

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →