
Written on 3rd September 2026 by Carter Phan.
Cả 3 đều giải quyết bài toán: Server muốn cập nhật dữ liệu cho Client gần real-time như thế nào?
Ví dụ:
User đang xem:
Order #123
Trạng thái:
PENDING → PROCESSING → SHIPPING → DELIVERED
Có 3 cách phổ biến:
Polling
Client hỏi liên tục:
Client → Server: status?
Client → Server: status?
Client → Server: status?
SSE
Client mở 1 connection:
Client ← Server
← update
← update
← update
WebSocket
Hai chiều:
Client ↔ Server
↔
↔
Client chủ động hỏi server định kỳ để xem có data mới không.
Client Server
│ │
│ GET /order/123 │
│────────────────────────>│
│ PENDING │
│<────────────────────────│
│ │
│ wait 5 seconds │
│ │
│ GET /order/123 │
│────────────────────────>│
│ PENDING │
│<────────────────────────│
│ │
│ wait 5 seconds │
│ │
│ GET /order/123 │
│────────────────────────>│
│ SHIPPING │
│<────────────────────────│
Ví dụ frontend:
setInterval(async () => {constres=awaitfetch('/api/order/123');constorder=awaitres.json();updateUI(order);
},5000);
Mỗi 5 giây:
Request → Response
Request → Response
Request → Response
Dùng HTTP bình thường:
GET /api/orders/123
Không cần:
HTTP request thông thường có thể đi qua:
CDN
Load Balancer
API Gateway
Reverse Proxy
Có thể inspect request bằng:
Browser DevTools
curl
Postman
logs
Giả sử:
100,000 users
poll mỗi 5 giây
Server nhận:
100,000 / 5
= 20,000 requests/sec
dù 99% request không có data mới.
Client
↓
"Anything new?"
↓
Server
↓
"No"
→ wasted requests.
Có một biến thể:
Server giữ request mở cho đến khi có data mới hoặc timeout.
Client Server
│ │
│ GET /updates │
│────────────────────────>│
│ │
│ │ wait...
│ │ wait...
│ │
│ NEW DATA │
│<────────────────────────│
│ │
│ GET /updates │
│────────────────────────>│
Khác polling:
Polling:
request → response ngay
↓
wait
↓
request
Long polling:
request ───────────────→
wait
wait
↓
response
Long polling giảm số request rỗng nhưng vẫn có connection/request lifecycle overhead.
Client mở một HTTP connection lâu dài và server chủ động push data xuống client.
Client Server
│ │
│ GET /events │
│────────────────────────>│
│ │
│ │
│ event 1 │
│<────────────────────────│
│ │
│ event 2 │
│<────────────────────────│
│ │
│ event 3 │
│<────────────────────────│
Connection:
1 HTTP request
↓
persistent connection
↓
many server → client events
Đây là điểm quan trọng.
SSE thường dùng:
Content-Type: text/event-stream
Ví dụ:
GET /events
event: order-status
data: {"status":"SHIPPING"}
event: order-status
data: {"status":"DELIVERED"}
Browser có API native:
constsource=newEventSource('/events');source.onmessage= (event) => {console.log(event.data);
};
Client ──────── HTTP request ────────→ Server
Client ←──── event ←──── event ←──── Server
Sau khi connection được mở:
Server → Client
là direction chính.
Nếu client cần gửi command:
Client → Server
thường dùng:
POST /api/...
hoặc một HTTP request khác.
Rất hợp với:
Server
↓
"New notification"
↓
Browser
PENDING
↓
PROCESSING
↓
SHIPPING
Export 10%
↓
Export 30%
↓
Export 70%
↓
Done
news
stock price
activity feed
monitoring
Đặc biệt:
Server cần push, nhưng client không cần gửi message liên tục.
WebSocket tạo một persistent, bidirectional connection giữa client và server.
Client Server
│ │
│──── connection ─────────>│
│ │
│──── message ────────────>│
│<──── message ────────────│
│──── message ────────────>│
│<──── message ────────────│
Hai bên đều có thể gửi data bất cứ lúc nào.
HTTP:
Request
↓
Response
↓
Connection/request ends
WebSocket:
Handshake
↓
Persistent connection
↓
Message ↔ Message
↓
Connection close
Sau khi connection established:
Client ↔ Server
không cần tạo HTTP request mới cho mỗi message.
Khi cần real-time + bidirectional.
User A → message → Server
↓
User B
Player position
↕
Server
↕
Other players
User A ─┐
├── WebSocket Server
User B ─┤
│
User C ─┘
Client ↔ Market Server
Ví dụ IoT/device control:
Browser
↕
Backend
↕
Device
| Polling | SSE | WebSocket | |
| Connection | Short-lived | Persistent | Persistent |
| Direction | Client → Server | Server → Client | Bidirectional |
| Protocol | HTTP | HTTP | WebSocket |
| Real-time | ❌/⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Complexity | ⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
| Browser support | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Auto reconnect | App phải tự làm | Built-in browser support | App/library thường xử lý |
| Binary data | ❌ | Text-oriented | ✅ |
| Chat | ❌ | ⚠️ | ✅ |
| Notifications | ✅ | ✅ | ✅ |
| Live dashboard | ✅ | ✅ | ✅ |
| Multiplayer | ❌ | ❌ | ✅ |
Polling
"Hey server, anything new?"
↓
↓
↓
SSE
"Server, tell me when something happens."
↓
↓
↓
WebSocket
"Let's keep a conversation open."
↕
↕
↕
Hoặc:
Polling = ASK
SSE = LISTEN
WebSocket = TALK
Giả sử Polling mỗi:
5 seconds
Server update ngay sau khi client vừa poll:
Client poll ──→ Server
↓
no change
1 sec later
↓
data updated
4 sec later
Client poll ──→ Server
↓
new data
Client có thể mất gần:
0 ~ 5 seconds
mới biết.
SSE:
Server data changes
↓
Server push immediately
↓
Client
WebSocket cũng vậy.
Đây là điểm quan trọng khi system design.
Nếu requirement:
"User cần biết export đã hoàn thành."
Không nhất thiết:
WebSocket
Có thể:
SSE
hoặc thậm chí:
Polling every 5-10 seconds
Nếu export mất:
2 minutes
thì latency 5 giây có thể hoàn toàn acceptable.
Giả sử:
Server → Client
Bạn có thể chọn SSE.
Ví dụ:
Export Job
↓
Worker
↓
Progress update
↓
SSE
↓
Browser
10%
↓
30%
↓
70%
↓
100%
Không cần WebSocket.
Tại sao?
Client → Server
không cần real-time.
Khi client cũng cần gửi event liên tục:
Client ↔ Server
Ví dụ chat:
Client → "Hello"
Server → deliver
Client → "Typing..."
Server → broadcast
Client → "Read"
Server → update
Hoặc game:
Player position → Server
Server → game state
Server → other players
SSE không phù hợp bằng WebSocket cho pattern này.
Đây là phần quan trọng hơn việc biết API.
Polling:
Load Balancer
↓
Request
↓
Any server
Stateless HTTP → scale tương đối đơn giản.
SSE/WebSocket:
Client
│
│ persistent connection
↓
Server A
Connection đang nằm trên Server A.
Nếu có:
Server A
Server B
Server C
thì phải nghĩ đến:
Client 1 ──→ A
Client 2 ──→ B
Client 3 ──→ C
Nếu Server A nhận event:
Event
↓
Server A
nhưng user cần nhận event đang connect vào B:
A ──X──→ B
Cần một shared messaging layer:
Redis Pub/Sub
│
┌────────┼────────┐
↓ ↓ ↓
Server A Server B Server C
↓ ↓ ↓
Clients Clients Clients
Hoặc:
Kafka
RabbitMQ
Redis Streams
tùy use case.
Ví dụ chat:
User A
↓
Server A
User B
↓
Server B
A gửi message:
A → Server A
Server A cần deliver cho B:
Server A
↓
Message Broker
↓
Server B
↓
User B
Đây là lý do WebSocket architecture production thường không chỉ là:
Browser ↔ WebSocket Server
mà là:
Clients
↓
Load Balancer
↓
WebSocket Servers
↓
Redis/Kafka/RabbitMQ
↓
Business Services
Persistent connection tạo thêm vấn đề:
10,000 users
↓
10,000 connections
Server phải quản lý:
Network có thể mất:
Client
│
│ WebSocket
│
X──── Network lost
Client cần:
disconnect
↓
backoff
↓
reconnect
↓
resync state
Ví dụ:
1s
↓
2s
↓
4s
↓
8s
Không nên reconnect liên tục:
1000 clients
↓
network outage
↓
1000 reconnects cùng lúc
→ thundering herd.
Browser EventSource hỗ trợ reconnect.
Server
↓
SSE connection
X
↓
Browser reconnect
SSE cũng có cơ chế Last-Event-ID để server/client có thể hỗ trợ tiếp tục từ event cuối đã nhận, nếu application thiết kế event stream phù hợp.
Persistent connection cần detect dead connection.
Ví dụ WebSocket:
Client ← ping/pong → Server
Nếu:
no pong
→ close connection.
SSE thường dựa vào việc server gửi periodic comment/heartbeat hoặc event để tránh idle timeout ở proxy/load balancer.
Giả sử server gửi:
1000 events/sec
nhưng client chỉ xử lý:
100 events/sec
Producer
↓
1000/sec
↓
Connection
↓
Client
↑
100/sec
Data có thể:
buffer ↑
memory ↑
latency ↑
Đây là vấn đề backpressure.
Đặc biệt quan trọng với:
Cần server push?
│
├── No
│ ↓
│ Polling
│
└── Yes
│
↓
Client cần gửi
real-time message?
│
├── No
│ ↓
│ SSE
│
└── Yes
↓
WebSocket
Nhưng:
Polling
vẫn có thể là lựa chọn tốt nếu:
update không thường xuyên
+
latency vài giây acceptable
+
muốn simplicity
Order status
Nếu update mỗi vài giây/phút:
→ Polling
Nếu muốn server push ngay:
→ SSE
WebSocket thường là overkill nếu client không cần bidirectional real-time.
User A ↔ User B
→ WebSocket
CPU
Memory
Requests
Errors
Server chủ động gửi:
10%
↓
20%
↓
15%
→ SSE rất hợp.
Nếu dashboard cũng gửi real-time commands:
Client → pause service
Client → restart
Server → metrics
→ WebSocket có thể hợp hơn, hoặc HTTP commands + SSE events.
Player
↕
Game Server
↕
Other players
→ WebSocket
Browser
│
│ HTTP
↓
Load Balancer
↓
API Server
↓
Redis / DB
Browser
│
│ persistent HTTP
↓
Load Balancer
↓
SSE Server
↑
│
Redis / Kafka / Event Bus
┌─────────────┐
│ Browser │
└──────┬──────┘
│
WebSocket
│
Load Balancer
│
┌─────────┼─────────┐
↓ ↓ ↓
WS Server WS Server WS Server
└─────────┼─────────┘
↓
Message Broker
↓
Business Services