Giao diện
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
| Metric | Giá trị |
|---|---|
| Messages/day | 2 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ự.