WebSocket so với Server-Sent Events: API thời gian thực nào tốt hơn?

Ashley Innocent

Ashley Innocent

13 tháng 3 2026

WebSocket so với Server-Sent Events: API thời gian thực nào tốt hơn?

Apidog cho doanh nghiệp

Triển khai tại chỗ

SSO & RBAC

Tuân thủ SOC 2

Khám phá Apidog Enterprise

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.

💡
Nếu bạn đang xây dựng hoặc thử nghiệm các API thời gian thực, Apidog hỗ trợ cả kiểm thử SSE và WebSocket. Bạn có thể kiểm thử các luồng sự kiện, xác thực định dạng tin nhắn và mô phỏng các kịch bản kết nối lại.
nút

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 Đô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)
Độ 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:

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:

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:

3. Các kịch bản kiểm thử:

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:

4. Các kịch bản kiểm thử:

Khi nào nên sử dụng từng loại

Sử dụng SSE khi:

Ví dụ:

Sử dụng WebSocket khi:

Ví dụ:

Đừ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ở.

Thực hành thiết kế API trong Apidog

Khám phá cách dễ dàng hơn để xây dựng và sử dụng API