12/04/2026 18:16pm

EP.113 Building an Advanced Multi-room Chat System with Go and WebSocket
#Golang The Series
#redis pubsub
#Multi-room Chat
#WebSocket
#Go
In a typical real-time chat system, users often communicate within a single shared room. But as systems grow — like team chats, support channels, game lobbies, or customer groups we must scale the architecture to support multi-room communication with clear room-level separation and control.
This episode will guide you through building a scalable multi-room chat system with production-ready features such as:
- Room-based message broadcasting
- Room-level user management
- Cross-instance communication using Redis Pub/Sub
🧩 Multi-room Chat System Architecture
Client → WebSocket Gateway → Redis Pub/Sub → Chat Rooms
Core Components
- Room Manager: Handles room creation, deletion, and member tracking
- Connection Manager: Tracks user connections per room
- Redis Pub/Sub: Distributes messages across instances for scaling
⚙️ Basic Data Structure
type Client struct {
ID string
Conn *websocket.Conn
RoomID string
}
type Room struct {
ID string
Clients map[*Client]bool
}
Room Manager Setup
type RoomManager struct {
Rooms map[string]*Room
mu sync.Mutex
}
func NewRoomManager() *RoomManager {
return &RoomManager{
Rooms: make(map[string]*Room),
}
}
💡 Room & Member Management Functions
func (rm *RoomManager) JoinRoom(roomID string, client *Client) {
rm.mu.Lock()
defer rm.mu.Unlock()
room, ok := rm.Rooms[roomID]
if !ok {
room = &Room{ID: roomID, Clients: make(map[*Client]bool)}
rm.Rooms[roomID] = room
}
room.Clients[client] = true
client.RoomID = roomID
}
func (rm *RoomManager) LeaveRoom(roomID string, client *Client) {
rm.mu.Lock()
defer rm.mu.Unlock()
if room, ok := rm.Rooms[roomID]; ok {
delete(room.Clients, client)
if len(room.Clients) == 0 {
delete(rm.Rooms, roomID)
}
}
}
✅ When a user joins a room, the system checks if the room exists if not, it will automatically create it.
📡 Broadcasting Messages within a Room
func (rm *RoomManager) Broadcast(roomID string, msg string) {
rm.mu.Lock()
defer rm.mu.Unlock()
if room, ok := rm.Rooms[roomID]; ok {
for c := range room.Clients {
c.Conn.WriteMessage(websocket.TextMessage, []byte(msg))
}
}
}
✅ All users in the same room will receive messages in real-time.
🔄 Scaling with Redis Pub/Sub
When you deploy multiple server instances, you'll need to synchronize messages across them. That’s where Redis Pub/Sub comes in.
func subscribeRedis(rm *RoomManager, rdb *redis.Client) {
sub := rdb.Subscribe(context.Background(), "chat_channel")
for msg := range sub.Channel() {
var data struct {
RoomID string `json:"room_id"`
Message string `json:"message"`
}
json.Unmarshal([]byte(msg.Payload), &data)
rm.Broadcast(data.RoomID, data.Message)
}
}
func publishMessage(rdb *redis.Client, roomID, msg string) {
data, _ := json.Marshal(map[string]string{
"room_id": roomID,
"message": msg,
})
rdb.Publish(context.Background(), "chat_channel", data)
}
✅ Now, no matter which server a user is connected to, the message will reach all clients across all machines.
🧠 Advanced Optimization Tips
| Category | Recommendations |
|---|---|
| Scalability | Use Redis or NATS for message distribution |
| Room Types | Support types like global, private, team |
| User Management | Store per-room users in Redis hash |
| Persistence | Save chat messages to a database |
| Monitoring | Track metrics such as connections per room |
🚀 Challenge: Build Your Own!
Try building your own multi-room chat system using Go + WebSocket, and integrate Redis Pub/Sub to support distributed message broadcasting.
Mastering this architecture means you're ready to build enterprise-grade real-time chat systems!
🌟 Coming Next: EP.114 Multi-device & Multi-session Management
In the next episode, we’ll explore how to manage multiple devices and sessions per user, design seamless session control, prevent duplication, and support safe, concurrent access across mobile and desktop all over WebSocket! 📱💻