REST vs gRPC vs WebSocket vs SSE — chọn cái nào
Bốn mô hình giao tiếp client-server: REST request/response stateless, gRPC HTTP/2 binary streaming, WebSocket full-duplex bền vững, SSE server-to-client một chiều. Bảng so sánh và cây quyết định để chọn đúng cho từng use-case.
TL;DR: Không có một giao thức "tốt nhất" — mỗi trong 4 mô hình giải quyết bài toán khác nhau. REST (HTTP + JSON, stateless, cacheable) là lựa chọn mặc định cho hầu hết public API vì đơn giản, dễ debug, và cache tốt. gRPC (HTTP/2 + Protocol Buffers nhị phân, strongly-typed, bidirectional streaming) dùng cho service-to-service nội bộ khi cần hiệu năng cao hoặc streaming. WebSocket nâng cấp HTTP thành kết nối full-duplex bền vững — cần khi cả client lẫn server phải gửi dữ liệu bất kỳ lúc nào (chat, collab). SSE (Server-Sent Events) dùng HTTP thường, một chiều server→client với tự reconnect — đủ dùng và đơn giản hơn nhiều khi chỉ cần push từ server (notification, live feed). Pitfall phổ biến: dùng polling khi nên dùng SSE/WebSocket, hoặc dùng WebSocket khi SSE đủ.
1. Tại sao không phải lúc nào cũng dùng REST?
REST hoạt động hoàn hảo cho hầu hết use-case — request/response, cacheable, stateless, mọi ngôn ngữ đều có HTTP client. Nhưng có những bài toán REST xử lý kém:
- Notification realtime: server cần push thông báo tới client ngay khi có sự kiện. REST buộc client poll liên tục — tốn băng thông, tốn server, và vẫn có độ trễ.
- Chat / collaborative editing: cả client lẫn server cần gửi bất kỳ lúc nào. REST chỉ cho phép client initiate — server không thể "gọi" client.
- Service-to-service nhiều field, nhiều call: serialization JSON chậm, schema không enforce, debug lỗi type phát hiện muộn (runtime thay vì compile-time).
Hiểu vì sao mỗi mô hình tồn tại giúp bạn chọn đúng — không phải chọn theo trend.
2. REST — request/response stateless
REST (Representational State Transfer) không phải giao thức — là architectural style do Roy Fielding định nghĩa trong dissertation 2000. Khi người ta nói "REST API", họ thường muốn nói HTTP API với một số convention:
- Resource-based URL:
GET /users/123,POST /orders,DELETE /products/42. - Stateless: mỗi request phải chứa đủ thông tin để server xử lý; server không lưu trạng thái session giữa các request (state nằm ở client hoặc DB).
- HTTP method có ngữ nghĩa: GET (đọc, safe, idempotent), POST (tạo mới), PUT (replace, idempotent), PATCH (partial update), DELETE (xóa, idempotent).
- JSON body là format phổ biến nhất, dù REST không bắt buộc.
Strengths:
- Đơn giản, mọi ngôn ngữ, mọi tool đều hiểu HTTP.
- Cacheable: GET response có thể cache ở CDN, browser — giảm load server tự nhiên.
- Debuggable:
curl, browser DevTools, Postman đều hoạt động. - Human-readable: JSON body đọc được bằng mắt, log dễ trace.
Limitations:
- Request/response theo hướng client→server: server không thể initiate.
- Mỗi request độc lập — overhead HTTP header mỗi lần.
- Schema không enforce ở transport level: type mismatch phát hiện ở runtime.
- Không phù hợp cho realtime (phải poll hoặc dùng giao thức khác).
3. gRPC — HTTP/2 + Protocol Buffers
gRPC (gRPC Remote Procedure Calls) là framework RPC của Google, open-source 2016. Thay vì gọi URL REST, client gọi function trên server như gọi function local (abstraction).
Cơ chế bên dưới:
- Transport: HTTP/2 — tận dụng multiplexing (nhiều call song song trên 1 kết nối), bidirectional streaming, HPACK header compression. Đây là lý do gRPC efficient với high-volume service-to-service traffic.
- Serialization: Protocol Buffers (protobuf) — binary format do Google thiết kế. Schema được định nghĩa trong file
.proto:
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse);
rpc ListUsers (ListUsersRequest) returns (stream UserResponse);
rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}
message GetUserRequest {
string user_id = 1;
}
message UserResponse {
string user_id = 1;
string name = 2;
int32 age = 3;
}
Từ file .proto, toolchain sinh code client/server cho nhiều ngôn ngữ (Go, Java, Python, TypeScript…). Client gọi userService.getUser({ userId: "123" }) — không viết URL, không parse JSON.
Bốn loại call trong gRPC:
| Loại | Mô tả | Use-case |
|---|---|---|
| Unary | 1 request → 1 response (giống REST) | Hầu hết API thông thường |
| Server streaming | 1 request → nhiều response | Tải file lớn, live feed |
| Client streaming | Nhiều request → 1 response | Upload file, batch insert |
| Bidirectional streaming | Nhiều request + nhiều response đan xen | Chat, collaborative, telemetry |
Strengths:
- Strongly-typed: lỗi type bắt ở compile-time, không phải runtime.
- Hiệu năng: protobuf nhỏ hơn JSON 3-10 lần; HTTP/2 multiplexing; ít overhead.
- Code generation: không phải viết HTTP client bằng tay; schema làm "contract" giữa service.
- Streaming tự nhiên: bidirectional streaming là first-class, không phải hack sau.
Limitations:
- Không native từ browser: browser không expose HTTP/2 frames trực tiếp → cần grpc-web (proxy) hoặc grpc-gateway (convert REST ↔ gRPC) để dùng từ JavaScript browser.
- Debug khó hơn: binary format không đọc được bằng mắt; cần tool như
grpcurlhoặc Postman gRPC. - Không cacheable ở tầng HTTP thông thường.
- Overhead setup: cần học protobuf, thiết lập code generation pipeline.
Mỗi gRPC call là một HTTP/2 stream. Khi service A gọi 50 request đồng thời tới service B, tất cả 50 call đi trên 1 kết nối TCP qua 50 stream HTTP/2. Không cần connection pool lớn. Đây là lý do gRPC đặc biệt hiệu quả trong kiến trúc microservice với nhiều inter-service call — so với REST cần nhiều kết nối TCP hoặc connection pooling phức tạp.
4. WebSocket — full-duplex bền vững
WebSocket (RFC 6455) là giao thức tạo ra kết nối full-duplex (hai chiều đồng thời) bền vững trên cùng TCP connection. Client và server có thể gửi message cho nhau bất kỳ lúc nào, không cần request trước.
Cơ chế upgrade:
WebSocket bắt đầu như một HTTP request bình thường rồi "upgrade":
Client --> Server:
GET /ws HTTP/1.1
Host: api.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Server --> Client:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sau 101 Switching Protocols, kết nối không còn là HTTP — nó trở thành WebSocket frame protocol. Server có thể push message bất kỳ lúc nào; client cũng vậy. Kết nối giữ cho tới khi một bên đóng.
Strengths:
- Thực sự bidirectional: server push bất kỳ lúc nào không cần client hỏi.
- Low latency: không overhead HTTP mỗi message (chỉ vài byte header WebSocket frame).
- Stateful: server biết client nào đang connect — dễ quản lý session.
Limitations:
- Stateful server: server phải duy trì state cho mỗi kết nối đang mở — scale ngang (nhiều server instance) cần sticky session hoặc shared state (Redis pub/sub).
- Load balancer phức tạp hơn: cần hỗ trợ connection upgrade và long-lived TCP connections.
- Reconnect tay: nếu kết nối đứt (mạng yếu, server restart), client phải tự detect và reconnect.
- Không cacheable, không RESTful: mỗi server là một ốc đảo state.
5. SSE — server push một chiều, đơn giản hơn
SSE (Server-Sent Events) là cơ chế server push một chiều (server→client) chạy trên HTTP thông thường. Không phải giao thức mới — chỉ là HTTP response mở dài với Content-Type: text/event-stream, gửi dữ liệu theo từng "event".
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
data: {"type":"notification","message":"New order #1234"}\n\n
data: {"type":"stock_update","symbol":"AAPL","price":185.3}\n\n
event: alert
data: Server will restart in 60s\n\n
Client dùng EventSource API (native trong browser, không cần library):
const source = new EventSource('/api/events');
source.onmessage = (e) => console.log(JSON.parse(e.data));
source.addEventListener('alert', (e) => showAlert(e.data));
// Auto-reconnect built-in khi mat ket noi
Strengths:
- Cực đơn giản: HTTP thông thường — mọi proxy, CDN, load balancer đều hiểu.
- Auto-reconnect:
EventSourcetự reconnect khi mất kết nối, kèmLast-Event-IDđể server biết resume từ đâu. - Không cần library phức tạp: native browser API đã đủ.
- Tương thích HTTP/2: trên HTTP/2, nhiều SSE stream đi trên 1 kết nối TCP — không tốn connection mới.
Limitations:
- Chỉ server→client: client không thể gửi data qua cùng kết nối (dùng REST request riêng nếu cần).
- Text only: format
text/event-streamlà text; binary data phải base64 encode. - Giới hạn browser: IE11 không hỗ trợ (dùng polyfill); trên một số mạng doanh nghiệp, proxy có thể buffer response → SSE bị chậm.
6. So sánh và cây quyết định
| REST | gRPC | WebSocket | SSE | |
|---|---|---|---|---|
| Mô hình | Request/Response | RPC (Unary hoặc Streaming) | Full-duplex | Server push |
| Chiều dữ liệu | Client→Server | Cả hai | Cả hai | Server→Client |
| Format | JSON (thường) | Protobuf (binary) | Bất kỳ | Text (event-stream) |
| Transport | HTTP/1.1 hoặc 2 | HTTP/2 | TCP (sau upgrade) | HTTP/1.1 hoặc 2 |
| Cache | Tốt (GET) | Không | Không | Không |
| Browser native | Có | Cần grpc-web | Có | Có (EventSource) |
| Reconnect | Tay | Tay | Tay | Tự động |
| Dùng khi nào | Public API, CRUD | Service-to-service nội bộ | Chat, collab realtime | Notification, live feed |
flowchart TD
A["Bai toan giao tiep?"]
A --> B{"Server can push<br/>cho client?"}
B -->|Khong| C["REST<br/>(request/response)"]
B -->|Co| D{"Client cung<br/>can gui lai?"}
D -->|Khong chi doc| E["SSE<br/>(server push 1 chieu)"]
D -->|Co| F{"Internal service<br/>hay browser?"}
F -->|Browser / public| G["WebSocket<br/>(full-duplex)"]
F -->|Internal microservice| H{"Can streaming<br/>hieu nang cao?"}
H -->|Co| I["gRPC<br/>(HTTP/2 + protobuf)"]
H -->|Khong| CHướng dẫn quick:
- Public CRUD API (mobile app, SPA, third-party integration) → REST. Dễ document, dễ debug, cache tốt.
- Notification, alert, live dashboard (giá cổ phiếu, trạng thái đơn hàng, activity feed) → SSE. Đơn giản hơn WebSocket khi chỉ cần server push.
- Chat, collaborative editing, multiplayer game (Google Docs, Figma, Discord) → WebSocket. Cần bidirectional thật sự và low-latency.
- Service-to-service trong Kubernetes cluster (order-service gọi inventory-service, payment-service) → gRPC. Hiệu năng cao, strongly-typed, streaming native.
7. Pitfall thường gặp
❌ Polling khi nên dùng SSE/WebSocket:
// SAI — poll moi 1s:
setInterval(async () => {
const res = await fetch('/api/notifications');
// ...
}, 1000);
// DUNG — SSE:
const source = new EventSource('/api/notifications');
source.onmessage = (e) => handleNotification(e.data);
Poll mỗi 1 giây với 10.000 user = 10.000 request/giây chỉ để "hỏi có gì mới không". SSE chỉ tốn 1 kết nối/user và server push khi có sự kiện thật sự.
❌ Dùng WebSocket khi SSE đủ: WebSocket complex hơn SSE đáng kể (reconnect tay, sticky session, scaling). Nếu bạn chỉ cần server gửi notification cho client (không cần client gửi data qua cùng kết nối), SSE đơn giản hơn nhiều và đủ dùng.
❌ gRPC từ browser mà không có grpc-web:
Browser không expose HTTP/2 frame trực tiếp — không thể gọi gRPC endpoint thẳng. Cần grpc-web proxy (thường là Envoy) hoặc grpc-gateway (generate REST → gRPC adapter). Thường dễ hơn là expose REST endpoint từ backend và dùng gRPC chỉ cho internal service communication.
❌ WebSocket không xử lý reconnect: WebSocket không tự reconnect. Mạng chập chờn, server deploy, load balancer timeout — kết nối đứt và message mất. Cần implement reconnect logic với exponential backoff, hoặc dùng library như Socket.IO (đã handle sẵn).
Với HTTP/1.1, mỗi SSE connection chiếm 1 kết nối TCP. Browser giới hạn 6 kết nối/origin — tức chỉ mở được 6 SSE stream đồng thời từ cùng origin. Với HTTP/2, nhiều SSE stream đi trên 1 kết nối TCP nhờ multiplexing — giới hạn này không còn. Đảm bảo server hỗ trợ HTTP/2 nếu cần nhiều SSE stream đồng thời.
8. Liên hệ các bài khác
- Bài 04 — HTTP/2 multiplexing: gRPC phụ thuộc vào HTTP/2 stream cho hiệu năng — multiplexing và bidirectional streaming là lý do gRPC chọn HTTP/2 làm transport.
- Bài 01 — HTTP anatomy, method & status: REST method (GET/POST/PUT/DELETE) và status code là nền tảng trực tiếp của bài này — method semantics quyết định contract của REST API.
- Bài 02 — HTTP headers quan trọng: header
Upgrade: websocketvàConnection: Upgradetrong WebSocket handshake;Cache-Controllà lý do REST GET cacheable tốt. - Bài 07 — Caching, CDN & proxy: REST GET response cache được ở CDN là lợi thế lớn; WebSocket/SSE/gRPC không cacheable — đây là một yếu tố trong quyết định chọn giao thức.
9. 📚 Deep Dive — tài liệu gốc
Đọc khi muốn đi tới gốc:
- RFC 6455 — WebSocket Protocol — đặc tả đầy đủ WebSocket: handshake upgrade (Mục 4), frame format (Mục 5), closing handshake (Mục 7).
- W3C Server-Sent Events — SSE là HTML spec (không phải RFC riêng):
EventSourceAPI, event stream format, reconnect vớiLast-Event-ID. - gRPC documentation — guide chính thức:
Basics Tutorialcho từng ngôn ngữ,Conceptsgiải thích 4 loại call,Best Practicescho production. - Roy Fielding Dissertation Chapter 5 — định nghĩa gốc REST architectural style: 6 constraints (stateless, cacheable, uniform interface…). Đọc 1 lần để biết "REST thật" khác "HTTP API thường" thế nào.
10. Tóm tắt
- REST: HTTP + JSON, stateless, cacheable, bidirectional về hướng nhưng client-initiated — mặc định cho public API.
- gRPC: HTTP/2 + protobuf binary, strongly-typed, 4 loại call (unary + 3 streaming), code generation từ
.proto— tối ưu cho internal service-to-service, cần grpc-web cho browser. - WebSocket: upgrade HTTP thành kết nối TCP full-duplex bền vững — server và client gửi bất kỳ lúc nào; dùng cho chat, collab, game realtime; cần tự handle reconnect và state management.
- SSE: HTTP response mở dài,
text/event-stream, server push một chiều với auto-reconnect — đơn giản hơn WebSocket cho notification, live feed, dashboard. - Cây quyết định: không cần server push → REST; cần push, chỉ server gửi → SSE; cần bidirectional browser → WebSocket; internal microservice hiệu năng cao → gRPC.
- Pitfall chính: polling thay vì SSE/WebSocket (lãng phí); WebSocket khi SSE đủ (thêm complexity không cần); gRPC từ browser thiếu grpc-web.
11. Tự kiểm tra
Q1Vì sao REST không phù hợp cho notification realtime? Polling có phải giải pháp không?▸
Q2gRPC dùng Protocol Buffers thay JSON vì lý do gì? Tradeoff là gì?▸
.proto — lỗi type phát hiện ở compile-time. Trade-off: không readable bằng mắt (cần tool để decode), cần build step sinh code từ .proto, overhead setup pipeline. Phù hợp nhất cho internal service-to-service khi team control cả hai đầu — không phù hợp cho public API cần human-readable payload.Q3WebSocket handshake hoạt động thế nào? Vì sao dùng HTTP để khởi tạo?▸
Upgrade: websocket và Connection: Upgrade. Server nếu hỗ trợ trả về 101 Switching Protocols. Sau đó, cùng 1 TCP connection chuyển sang WebSocket frame protocol — không còn là HTTP. Dùng HTTP để khởi tạo vì: đi qua được firewall/proxy mở port 80/443, tận dụng được TLS từ HTTPS, và tương thích với cơ sở hạ tầng HTTP hiện có mà không cần mở port mới.Q4SSE khác WebSocket như thế nào? Khi nào chọn SSE thay WebSocket?▸
text/event-stream), có auto-reconnect built-in qua EventSource API. WebSocket là full-duplex (cả hai chiều), là giao thức riêng sau upgrade, cần tự handle reconnect. Chọn SSE khi: chỉ cần server push (notification, live feed, progress update, activity stream) và client không cần gửi data liên tục qua cùng kết nối. WebSocket chỉ cần khi cả hai chiều đều cần gửi thường xuyên (chat, collaborative editing, game). SSE đơn giản hơn đáng kể và thường bị bỏ qua oan.Q5gRPC từ browser cần grpc-web vì lý do kỹ thuật gì?▸
.proto file — browser gọi REST, backend nhận gRPC.Q6Bạn build app chat realtime. REST, gRPC, WebSocket, hay SSE? Giải thích lý do.▸
Bài tiếp theo: Caching, CDN & proxy — đưa nội dung tới gần user
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên