Tóm tắt
Sử dụng Server-Sent Events (SSE) cho các cập nhật một chiều từ máy chủ đến máy khách như thông báo và nguồn cấp dữ liệu trực tiếp. Sử dụng WebSocket cho giao tiếp hai chiều như trò chuyện và chơi game. SSE đơn giản hơn và hoạt động qua HTTP. WebSocket phức tạp hơn nhưng hỗ trợ nhắn tin hai chiều. PetstoreAPI hiện đại triển khai cả hai cho các trường hợp sử dụng thời gian thực khác nhau.
Giới thiệu
Bạn cần cập nhật thời gian thực trong API của mình. Trạng thái của một thú cưng thay đổi từ “có sẵn” sang “đã được nhận nuôi”—khách hàng cần biết ngay lập tức. Bạn sử dụng WebSocket hay Server-Sent Events (SSE)?
Hầu hết các nhà phát triển mặc định sử dụng WebSocket vì nó “mạnh mẽ hơn”. Nhưng SSE thường là lựa chọn tốt hơn. Nó đơn giản hơn, hoạt động trên HTTP tiêu chuẩn và tự động xử lý việc kết nối lại. WebSocket bổ sung thêm sự phức tạp mà bạn có thể không cần.
PetstoreAPI hiện đại triển khai cả hai giao thức. SSE cho các cập nhật trạng thái thú cưng và thông báo đơn hàng. WebSocket cho đấu giá trực tiếp và trò chuyện thời gian thực. Mỗi giao thức phục vụ các trường hợp sử dụng khác nhau.
Trong hướng dẫn này, bạn sẽ tìm hiểu sự khác biệt giữa SSE và WebSocket, xem các ví dụ thực tế từ PetstoreAPI hiện đại và khám phá khi nào nên sử dụng từng giao thức.
Server-Sent Events (SSE) là gì?
SSE là một giao thức dựa trên HTTP để truyền tải các sự kiện từ máy chủ đến máy khách.
SSE hoạt động như thế nào
Máy khách mở một kết nối và nhận các sự kiện khi chúng xảy ra:
const eventSource = new EventSource('https://petstoreapi.com/v1/pets/notifications');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Pet update:', data);
};
eventSource.addEventListener('adoption', (event) => {
const data = JSON.parse(event.data);
console.log('Pet adopted:', data.petId);
});
Máy chủ gửi sự kiện:
GET /v1/pets/notifications
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
event: adoption
data: {"petId":"019b4132","userId":"user-456"}
event: status-change
data: {"petId":"019b4127","status":"AVAILABLE"}
Các tính năng của SSE
1. Giao tiếp một chiều
Máy chủ đẩy thông tin đến máy khách. Máy khách không thể gửi tin nhắn trở lại thông qua kết nối SSE (nhưng có thể sử dụng các yêu cầu HTTP thông thường).
2. Được xây dựng trên HTTP
Sử dụng HTTP tiêu chuẩn. Hoạt động thông qua các proxy, tường lửa và CDN.
3. Tự động kết nối lại
Nếu kết nối bị mất, trình duyệt sẽ tự động kết nối lại.
4. ID sự kiện để tiếp tục
Máy chủ có thể gửi ID sự kiện. Máy khách tiếp tục từ sự kiện cuối cùng đã nhận được:
id: 123
event: adoption
data: {"petId":"019b4132"}
id: 124
event: status-change
data: {"petId":"019b4127"}
Nếu bị ngắt kết nối, máy khách sẽ gửi tiêu đề Last-Event-ID: 124 để tiếp tục.
5. Giao thức đơn giản
Định dạng dựa trên văn bản. Dễ dàng gỡ lỗi với curl:
curl -N -H "Accept: text/event-stream" \
https://petstoreapi.com/v1/pets/notifications
Những hạn chế của SSE
1. Chỉ một chiều
Máy khách không thể gửi tin nhắn qua SSE. Cần các yêu cầu HTTP riêng biệt cho giao tiếp từ máy khách đến máy chủ.
2. Chỉ văn bản
SSE gửi văn bản. Dữ liệu nhị phân phải được mã hóa base64.
3. Giới hạn kết nối của trình duyệt
Trình duyệt giới hạn số lượng kết nối SSE trên mỗi miền (thường là 6). Đây không phải là vấn đề đối với hầu hết các ứng dụng.
4. Không có nén tích hợp
Nén HTTP hoạt động, nhưng không có nén cấp giao thức như WebSocket.
WebSocket là gì?
WebSocket là một giao thức song công hoàn toàn (full-duplex) hai chiều qua một kết nối liên tục.
WebSocket hoạt động như thế nào
Máy khách và máy chủ đều có thể gửi tin nhắn bất cứ lúc nào:
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// Gửi tin nhắn đến máy chủ
ws.send(JSON.stringify({
type: 'bid',
amount: 500
}));
// Nhận tin nhắn từ máy chủ
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Auction update:', data);
};
ws.onclose = () => {
console.log('Connection closed');
// Cần logic kết nối lại thủ công
};
Máy chủ có thể gửi bất cứ lúc nào:
{"type":"bid","userId":"user-456","amount":550}
{"type":"outbid","newAmount":550}
Máy khách có thể gửi bất cứ lúc nào:
{"type":"bid","amount":600}
{"type":"watch","petId":"019b4132"}
Các tính năng của WebSocket
1. Hai chiều
Cả máy khách và máy chủ đều có thể gửi tin nhắn bất cứ lúc nào. Giao tiếp hai chiều thực sự.
2. Độ trễ thấp
Kết nối liên tục. Không có chi phí HTTP cho mỗi tin nhắn. Lý tưởng cho chơi game, trò chuyện, cộng tác trực tiếp.
3. Hỗ trợ dữ liệu nhị phân
Có thể gửi dữ liệu nhị phân trực tiếp. Không cần mã hóa base64.
4. Giao thức tùy chỉnh
Sử dụng ws:// hoặc wss:// (an toàn). Không phải HTTP sau bắt tay ban đầu.
5. Dựa trên khung (Frame-based)
Tin nhắn được đóng khung (framed). Có thể gửi các tin nhắn một phần và ghép lại.
Những hạn chế của WebSocket
1. Cài đặt phức tạp
Yêu cầu máy chủ WebSocket. Phức tạp hơn các điểm cuối HTTP.
2. Kết nối lại thủ công
Không có kết nối lại tự động. Bạn phải triển khai logic thử lại.
3. Vấn đề với Proxy
Một số proxy của công ty chặn WebSocket. Các proxy HTTP không hiểu ws://.
4. Có trạng thái (Stateful)
Máy chủ phải theo dõi các kết nối. Khó mở rộng hơn HTTP không trạng thái.
5. Không có tính năng HTTP
Không thể sử dụng bộ nhớ đệm HTTP, mã trạng thái hoặc tiêu đề tiêu chuẩn sau khi bắt tay.
So sánh song song
| Tính năng | SSE | WebSocket |
|---|---|---|
| Hướng | Máy chủ → Máy khách | Hai chiều |
| Giao thức | HTTP | Tùy chỉnh (ws://) |
| Kết nối lại | Tự động | Thủ công |
| Hỗ trợ trình duyệt | Tất cả hiện đại | Tất cả hiện đại |
| Thân thiện với Proxy | Có | Đôi khi |
| Độ phức tạp | Đơn giản | Phức tạp |
| Dữ liệu nhị phân | Không (chỉ văn bản) | Có |
| Độ trễ | Thấp | Rất thấp |
| Khả năng mở rộng | Cao (không trạng thái) | Trung bình (có trạng thái) |
| Trường hợp sử dụng | Thông báo, nguồn cấp dữ liệu | Trò chuyện, chơi game, cộng tác |
Cách PetstoreAPI hiện đại sử dụng cả hai
PetstoreAPI hiện đại triển khai cả SSE và WebSocket cho các kịch bản khác nhau.
SSE cho cập nhật thú cưng
Điểm cuối (Endpoint): GET /v1/pets/notifications
const events = new EventSource(
'https://petstoreapi.com/v1/pets/notifications?userId=user-456'
);
events.addEventListener('adoption', (e) => {
const data = JSON.parse(e.data);
showNotification(`${data.petName} was adopted!`);
});
events.addEventListener('status-change', (e) => {
const data = JSON.parse(e.data);
updatePetStatus(data.petId, data.status);
});
Triển khai máy chủ:
app.get('/v1/pets/notifications', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const userId = req.query.userId;
// Đăng ký cập nhật thú cưng
const subscription = petUpdates.subscribe(userId, (event) => {
res.write(`event: ${event.type}\n`);
res.write(`data: ${JSON.stringify(event.data)}\n\n`);
});
req.on('close', () => {
subscription.unsubscribe();
});
});
Các trường hợp sử dụng:
- Thay đổi trạng thái thú cưng (có sẵn → đã được nhận nuôi)
- Thông báo đơn hàng (đã đặt, đã gửi, đã giao)
- Cập nhật tồn kho
- Thay đổi giá
WebSocket cho đấu giá trực tiếp
Điểm cuối (Endpoint): wss://petstoreapi.com/auctions/{auctionId}
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// Đặt giá thầu
function placeBid(amount) {
ws.send(JSON.stringify({
type: 'bid',
amount
}));
}
// Nhận cập nhật
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'bid':
updateCurrentBid(msg.amount, msg.userId);
break;
case 'outbid':
showOutbidNotification(msg.newAmount);
break;
case 'auction-end':
showAuctionResult(msg.winner);
break;
}
};
Triển khai máy chủ:
wss.on('connection', (ws, req) => {
const auctionId = req.params.auctionId;
const auction = auctions.get(auctionId);
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (msg.type === 'bid') {
const result = auction.placeBid(msg.userId, msg.amount);
// Phát sóng đến tất cả người tham gia
auction.participants.forEach(participant => {
participant.send(JSON.stringify({
type: 'bid',
userId: msg.userId,
amount: msg.amount
}));
});
}
});
});
Các trường hợp sử dụng:
- Đấu giá trực tiếp
- Trò chuyện thời gian thực với hỗ trợ
- Lập kế hoạch chăm sóc thú cưng cộng tác
- Cập nhật tồn kho trực tiếp trong quá trình bán hàng
Kiểm thử API thời gian thực với Apidog
Apidog hỗ trợ kiểm thử cả API SSE và WebSocket.
Kiểm thử SSE
1. Tạo yêu cầu SSE:
GET https://petstoreapi.com/v1/pets/notifications
Accept: text/event-stream
2. Xác thực sự kiện:
- Kiểm tra loại sự kiện
- Xác thực tải trọng JSON
- Kiểm tra hành vi kết nối lại
- Xác minh ID sự kiện
3. Các kịch bản kiểm thử:
- Mất kết nối
- Máy chủ khởi động lại
- Thứ tự sự kiện
- Tiếp tục từ sự kiện cuối cùng
Kiểm thử WebSocket
1. Tạo kết nối WebSocket:
wss://petstoreapi.com/auctions/019b4132
2. Gửi tin nhắn kiểm thử:
{"type":"bid","amount":500}
{"type":"watch","petId":"019b4132"}
3. Xác thực phản hồi:
- Kiểm tra định dạng tin nhắn
- Kiểm tra luồng hai chiều
- Xác minh xử lý kết nối
- Kiểm tra các kịch bản lỗi
4. Các kịch bản kiểm thử:
- Nhiều kết nối đồng thời
- Thứ tự tin nhắn
- Hết thời gian chờ kết nối
- Logic kết nối lại
Khi nào nên sử dụng từng loại
Sử dụng SSE khi:
- Cập nhật một chiều - Máy chủ đẩy thông tin đến máy khách, máy khách không cần gửi lại
- Cài đặt đơn giản - Muốn sử dụng cơ sở hạ tầng HTTP tiêu chuẩn
- Tự động kết nối lại - Không muốn triển khai logic thử lại
- Thân thiện với Proxy - Cần hoạt động thông qua tường lửa của công ty
- Thông báo - Cập nhật trạng thái, cảnh báo, nguồn cấp dữ liệu trực tiếp
Ví dụ:
- Thông báo nhận nuôi thú cưng
- Cập nhật trạng thái đơn hàng
- Thay đổi tồn kho
- Cảnh báo giá
- Nguồn cấp tin tức
Sử dụng WebSocket khi:
- Hai chiều - Cả máy khách và máy chủ gửi tin nhắn thường xuyên
- Độ trễ thấp là rất quan trọng - Chơi game, cộng tác thời gian thực
- Dữ liệu nhị phân - Gửi hình ảnh, âm thanh, video
- Giao thức tùy chỉnh - Cần kiểm soát hoàn toàn định dạng tin nhắn
- Tần suất tin nhắn cao - Hàng trăm tin nhắn mỗi giây
Ví dụ:
- Đấu giá trực tiếp
- Trò chuyện thời gian thực
- Trò chơi nhiều người chơi
- Chỉnh sửa cộng tác
- Truyền phát video trực tiếp
Đừng sử dụng WebSocket chỉ vì:
❌ “Nó tiên tiến hơn” - Phức tạp mà không mang lại lợi ích
❌ “Mọi người đều dùng nó” - SSE thường đơn giản hơn
❌ “Nó nhanh hơn” - SSE đủ nhanh cho hầu hết các trường hợp sử dụng
❌ “Nó là hai chiều” - Bạn có thực sự cần hai chiều không?
Kết luận
SSE và WebSocket đều cho phép giao tiếp thời gian thực, nhưng chúng được thiết kế cho các kịch bản khác nhau. SSE vượt trội trong các cập nhật một chiều từ máy chủ đến máy khách với khả năng tự động kết nối lại và tương thích HTTP. WebSocket tỏa sáng trong giao tiếp hai chiều, độ trễ thấp.
PetstoreAPI hiện đại cho thấy cách sử dụng hiệu quả cả hai giao thức. SSE cho thông báo và cập nhật trạng thái. WebSocket cho đấu giá trực tiếp và trò chuyện. Hãy chọn dựa trên trường hợp sử dụng của bạn, chứ không phải giao thức nào có vẻ “tốt hơn”.
Kiểm thử các API thời gian thực của bạn với Apidog để đảm bảo cả triển khai SSE và WebSocket hoạt động chính xác trong các kịch bản khác nhau.
Câu hỏi thường gặp
SSE có thể hoạt động qua tường lửa của công ty không?
Có. SSE sử dụng HTTP tiêu chuẩn, vì vậy nó hoạt động qua các proxy và tường lửa HTTP. WebSocket sử dụng một giao thức tùy chỉnh mà một số proxy có thể chặn.
WebSocket có nhanh hơn SSE không?
WebSocket có độ trễ thấp hơn một chút (không có chi phí HTTP cho mỗi tin nhắn), nhưng đối với hầu hết các ứng dụng, sự khác biệt là không đáng kể. SSE đủ nhanh cho các thông báo, nguồn cấp dữ liệu và cập nhật trạng thái.
Bạn xử lý việc kết nối lại SSE như thế nào?
Trình duyệt tự động xử lý việc kết nối lại. Gửi ID sự kiện từ máy chủ và máy khách sẽ tiếp tục từ sự kiện cuối cùng đã nhận được bằng cách sử dụng tiêu đề Last-Event-ID.
Bạn có thể sử dụng SSE với các ứng dụng di động không?
Có. iOS và Android hỗ trợ SSE thông qua các máy khách HTTP gốc hoặc thư viện. SSE hoạt động ở bất cứ đâu HTTP hoạt động.
Thời gian kết nối SSE tối đa là bao lâu?
Không có giới hạn cứng. Các kết nối SSE có thể duy trì mở vô thời hạn. Một số proxy hoặc bộ cân bằng tải có thể có thời gian chờ (thường là 30-60 giây), nhưng trình duyệt sẽ tự động kết nối lại.
WebSocket có thể gửi dữ liệu nhị phân không?
Có. WebSocket hỗ trợ cả khung văn bản và khung nhị phân. Bạn có thể gửi hình ảnh, âm thanh hoặc bất kỳ dữ liệu nhị phân nào mà không cần mã hóa base64.
Một trình duyệt có thể có bao nhiêu kết nối SSE?
Trình duyệt giới hạn số lượng kết nối SSE trên mỗi miền (thường là 6). Điều này hiếm khi là vấn đề—hầu hết các ứng dụng chỉ cần 1-2 kết nối SSE.
Bạn có cần một máy chủ đặc biệt cho SSE không?
Không. Bất kỳ máy chủ HTTP nào cũng có thể xử lý SSE. Chỉ cần đặt các tiêu đề chính xác (Content-Type: text/event-stream) và giữ kết nối mở.
