Skip to content

Thực hành: Thiết kế Real-time Chat System

🎯 Mục tiêu

🎯 Sau bài thực hành này, bạn sẽ:

  • Thiết kế chat system hỗ trợ 1-1 và group chat cho 50M DAU
  • So sánh WebSocket, SSE và long polling
  • Thiết kế message storage và delivery guarantee
  • Xây dựng presence system (online/offline/typing)

Yêu cầu hệ thống

Functional: Chat 1-1, group (tối đa 500), presence, read receipts, history, media.

Non-functional: Delivery dưới 200ms, at-least-once, đúng thứ tự, 50M DAU.

Ước lượng

MetricGiá trị
Messages/day2 billion (50M x 40)
Write QPS (peak)~70K
Concurrent connections~10M
Storage/day~400 GB

Kiến trúc

Thiết kế chi tiết

Message Flow (1-1)

User A gửi qua WebSocket → Chat Service gán Snowflake ID → persist Cassandra + push Kafka → lookup session User B → online: WebSocket, offline: push notification.

Presence System

Heartbeat mỗi 30s, Redis key user:{id}:online TTL 60s. Không heartbeat 60s → offline.

🎯 Scenario Choice

Fanout Strategy cho Group Messages

Group 500 members. User gửi message → deliver thế nào?

Fanout on Write: Copy inbox mỗi member. Read nhanh, 1 msg → 500 writes. Fanout on Read: Lưu 1 bản, pull khi mở app. Write rẻ, read phức tạp. Hybrid: Nhỏ (<50): write. Lớn (>50): read.

💡 WhatsApp dùng fanout on write (giới hạn 1024 members). Slack dùng hybrid vì có channels rất lớn.

Trắc nghiệm

🧠 Quiz

Câu 1: Tại sao Cassandra phù hợp cho message storage?

  • [ ] A) Cassandra hỗ trợ ACID tốt hơn MySQL
  • [x] B) Cassandra tối ưu write-heavy workload và horizontal scaling tự nhiên
  • [ ] C) Cassandra có query language mạnh hơn SQL
  • [ ] D) Cassandra tự động sort theo thời gian không cần index

💡 Chat write-heavy (2B msg/day). Cassandra dùng LSM-tree tối ưu write, partition by conversation_id + cluster by timestamp.

🧠 Quiz

Câu 2: Tại sao dùng heartbeat + TTL thay vì WebSocket disconnect?

  • [ ] A) Disconnect event không tồn tại
  • [x] B) Mất mạng đột ngột khiến server không nhận disconnect, heartbeat + TTL phát hiện offline chính xác
  • [ ] C) Heartbeat tốn ít bandwidth hơn
  • [ ] D) TTL giảm load trên Redis

💡 Mất mạng → TCP "treo" hàng phút. Heartbeat + TTL: không heartbeat 60s → offline.

🧠 Quiz

Câu 3: Đảm bảo message ordering trong group chat?

  • [ ] A) Dùng client-side timestamp
  • [ ] B) Auto-increment ID từ single MySQL
  • [x] C) Snowflake ID + Kafka partition by conversation_id đảm bảo order trong conversation
  • [ ] D) Sequence number tại mỗi WebSocket gateway

💡 Kafka đảm bảo order trong partition. Partition by conversation_id → mọi message cùng conversation đúng thứ tự.