# Scalable Chat System Using WebSockets, Load Balancer, Redis, and DynamoDB

In the world of modern chat applications, real-time communication is key. Whether it's for a messaging platform, collaborative tool, or gaming app, WebSockets offer a persistent and efficient channel for two-way communication. But what happens when you need to support thousands, or even millions, of users? That’s where a scalable architecture comes into play.

This blog will walk you through building a **highly scalable chat microservice** using **WebSockets**, **Redis**, **DynamoDB**, and a **load balancer**. We’ll explore how messages are routed, stored, and delivered efficiently across distributed services.

---

## 🌐 Key Components

### 1\. **WebSocket Servers**

These are the actual servers that handle WebSocket connections. Each server manages the live socket connections for the users it serves.

### 2\. **Redis (Pub/Sub + Socket Map)**

Redis is used to keep track of where users are connected (i.e., which socket server they are on) and to publish/subscribe messages across different WebSocket servers.

### 3\. **DynamoDB (Persistent Storage)**

Every chat message is stored in DynamoDB for durability and history retrieval.

### 4\. **Load Balancer**

Distributes new incoming socket connections evenly across your pool of WebSocket servers.

---

## 🗺️ Architecture Diagram

```plaintext
      ┌────────────┐
      │   Client   │
      └─────┬──────┘
            │
            ▼
      ┌────────────┐
      │ Load Balancer
      └─────┬──────┘
     ┌──────┼───────┐
     ▼      ▼       ▼
 ┌──────┐ ┌──────┐ ┌──────┐
 │ WS1  │ │ WS2  │ │ WS3  │   (WebSocket Servers)
 └──┬───┘ └──┬───┘ └──┬───┘
    │        │        │
    └────────┴────────┘
             ▼
         ┌────────┐
         │ Redis  │ (Pub/Sub + Socket Map)
         └────────┘
             ▼
        ┌──────────┐
        │ DynamoDB │ (Message Storage)
        └──────────┘
```

---

## 🧩 Setup Overview (Example):

| Component | Address |
| --- | --- |
| Load Balancer | `ws://`[`chat.example.com`](http://chat.example.com) |
| WebSocket Server 1 | `ws://`[`localhost:3001/ws`](http://localhost:3001/ws) |
| WebSocket Server 2 | `ws://`[`localhost:3002/ws`](http://localhost:3002/ws) |
| WebSocket Server 3 | `ws://`[`localhost:3003/ws`](http://localhost:3003/ws) |
| Redis | `redis://`[`localhost:6379`](http://localhost:6379) |
| Message API | [`http://localhost:4000`](http://localhost:4000) |
| DynamoDB | AWS managed |

---

## 🚪 User Connection Flow

### 1\. User A opens a chat app

* The frontend initiates a WebSocket connection:
    
    ```ts
    const socket = new WebSocket("ws://chat.example.com/ws?userId=userA");
    ```
    

### 2\. Load Balancer Routes the Request

* The load balancer forwards this request to one of the WebSocket servers (e.g., WS1).
    
* WebSocket handshake occurs.
    

### 3\. WebSocket Server (WS1) handles the connection

* Extracts `userId` from query
    
* Stores socket info in Redis:
    
    ```bash
    SET user:userA { socketId: xyz123, server: ws1 }
    ```
    

---

## 💬 Sending a Message: User A ➔ User B

### First Message:

1. User A sends:
    
    ```json
    {
      "type": "message",
      "to": "userB",
      "message": "Hi"
    }
    ```
    
2. WS1 receives the message
    
3. WS1 looks up userB in Redis:
    
    ```bash
    GET user:userB
    ```
    
4. Redis responds with `ws3` (userB is connected to WS3)
    
5. WS1 publishes to Redis pub/sub:
    
    ```json
    channel: messages:ws3
    payload: {
      "to": "userB",
      "message": "Hi",
      "from": "userA"
    }
    ```
    
6. WS3 receives it and sends it to userB via WebSocket
    
7. Message is also saved to DynamoDB
    

### Second Message:

* No new socket, no new Redis write
    
* Uses existing socket to send again
    
* Still does Redis lookup and pub/sub for delivery
    

---

## 📴 User Goes Offline

### 1\. WebSocket Disconnects

* Server receives `socket.on('close')`
    
* Removes user from Redis
    

### 2\. Another user sends them a message

* Redis `GET` returns null
    
* Server knows the user is offline
    
* Stores message in DynamoDB with delivery status
    

### 3\. User Comes Back Online

* New WebSocket connects
    
* Server updates Redis with new info
    
* Checks for undelivered messages
    
* Pushes them over the new socket
    

---

## 🚪 Is Load Balancer a Socket Server?

No. A **load balancer is not a socket server**. It only handles routing the initial HTTP/WS upgrade request to one of the WebSocket servers.

### What the Load Balancer Does:

* Routes initial WebSocket request
    
* Uses round-robin or sticky session
    
* After upgrade, client connects directly to WS server
    
* All further communication happens directly
    

---

## 🚚 Example With 10,000 Users

### Infrastructure:

| Component | Count |
| --- | --- |
| Load Balancer | 1 |
| WebSocket Servers | 3 |
| Redis | 1 |
| DynamoDB Table | 1 |

### Load Balancer Behavior:

* First user ➔ WS1
    
* Second user ➔ WS2
    
* Third user ➔ WS3
    
* Fourth user ➔ WS1 (round-robin)
    

Each WS server handles ~3333 users, while Redis tracks all socket associations.

---

## 📬 WebSocket Communication Endpoints

| Endpoint | Method | Description |
| --- | --- | --- |
| `/ws` | WS | WebSocket upgrade endpoint |
| `messages:wsX` | Redis PubSub | Channel for WS server `wsX` |
| `user:userId` | Redis Key | Stores socket ID and server for user |
| DynamoDB Table | \- | Stores persistent messages |

---

## 🚀 Horizontal Scaling and Communication Between Servers

Servers communicate using **Redis Pub/Sub**:

* When WS1 needs to message a user on WS3, it publishes to `messages:ws3` channel
    
* WS3 is subscribed and receives it
    

No direct server-to-server calls are needed — Redis handles the communication.

---

## ✨ Conclusion

This architecture enables your chat app to:

* Scale horizontally
    
* Deliver messages in real-time
    
* Store persistent history
    
* Handle user reconnects and offline messages
    

And with Redis + DynamoDB, you get both **speed** and **reliability**. The load balancer ensures users are evenly spread and connections stay persistent without needing to restart sockets.

If you're building a high-performance chat backend, this is the blueprint to follow.

---
