Người ta hay chọn WebSocket vì nghĩ nó "nhẹ hơn HTTP". Phần trước đo WebSocket; bài này đo SSE trên cùng máy chủ, cùng số client, và so hai bảng.
SSE trong Vert.x là ba dòng
r.get("/sse").handler(c -> {
var res = c.response();
res.setChunked(true)
.putHeader("Content-Type", "text/event-stream")
.putHeader("Cache-Control", "no-cache");
SSE.add(res);
res.closeHandler(v -> SSE.remove(res));
});
// gui mot su kien
res.write("data: " + json + "\n\n");
Không có bắt tay nâng cấp, không có giao thức riêng. Đây là một response HTTP bình thường không bao giờ kết thúc, và trình duyệt có sẵn EventSource để đọc nó.
Đo cạnh nhau
Cùng máy chủ, cùng máy, mỗi lần chạy trên tiến trình sạch:
| Số kết nối | Heap mỗi kết nối | RSS | Số luồng | Phát tin cho tất cả | |
|---|---|---|---|---|---|
| 1 000 | WebSocket | 4 263 B | 120 MiB | 37 | 7,5 ms |
| SSE | 4 071 B | 109 MiB | 37 | 9,1 ms | |
| 5 000 | WebSocket | 4 199 B | 150 MiB | 39 | 17,9 ms |
| SSE | 3 909 B | 172 MiB | 37 | 24,5 ms | |
| 10 000 | WebSocket | 4 037 B | 177 MiB | 39 | 21,3 ms |
| SSE | 3 905 B | 203 MiB | 39 | 54,9 ms |
Nên nếu bạn đang chọn WebSocket vì "nó nhẹ hơn", bảng trên nói rằng lý do đó không tồn tại.
Chỗ khác biệt thật
Phát tin ở quy mô lớn. Ở mười nghìn kết nối, WebSocket mất 21,3 ms còn SSE mất 54,9 ms — chậm hơn 2,5 lần. Lý do nằm ở khung dữ liệu: SSE gửi văn bản có cấu trúc (data: ...\n\n) qua chunked encoding, còn WebSocket gửi khung nhị phân gọn hơn. Ở một nghìn kết nối thì chênh lệch không đáng kể; ở trăm nghìn thì đáng.
Một chiều hay hai chiều. Đây là khác biệt quyết định. SSE chỉ đi từ máy chủ xuống client. Client muốn gửi gì thì phải mở một request HTTP riêng — vẫn được, nhưng là hai đường thay vì một.
Tự kết nối lại. EventSource của trình duyệt tự động kết nối lại khi đứt, và gửi kèm header Last-Event-ID để máy chủ biết gửi tiếp từ đâu. Với WebSocket bạn phải tự viết phần này, và tự viết phần này đúng thì khó hơn nó trông.
Đi qua hạ tầng cũ. SSE là HTTP thường, nên proxy, cân bằng tải, tường lửa doanh nghiệp đều hiểu. WebSocket cần nâng cấp giao thức, và không ít thiết bị trung gian vẫn cắt nó.
Chọn cái nào
| Bài toán | Nên dùng |
|---|---|
| Bảng điều khiển, thông báo, giá cổ phiếu, tiến độ tác vụ | SSE |
| Chat, game, cộng tác thời gian thực, gõ phím trực tiếp | WebSocket |
| Cần chạy qua proxy doanh nghiệp không kiểm soát được | SSE |
| Trăm nghìn kết nối trở lên, mỗi giây một lần phát | WebSocket |
Luật rút gọn: cần hai chiều thật thì WebSocket, còn lại thì SSE — vì nó đơn giản hơn, tự kết nối lại, và đo ra không tốn hơn.
Một cái bẫy chung
Cả hai đều giữ tham chiếu tới kết nối trong một tập, và cả hai đều cần dọn:
res.closeHandler(v -> SSE.remove(res)); // SSE
ws.closeHandler(v -> WS.remove(ws)); // WebSocket
Thiếu dòng này thì tập chỉ lớn lên, và vòng lặp phát tin ngày càng chậm vì nó đang gửi cho những kết nối đã chết từ lâu. Với 4 KB mỗi kết nối, một dịch vụ chạy vài tuần sẽ tự giải thích vì sao nó hết bộ nhớ.
Bài sau: xác thực và phân quyền trong Vert.x Web, và chi phí kiểm JWT trên mỗi request.
Thử ba mươi giây
# xem mot luong SSE bang mat
curl -N -H "Accept: text/event-stream" http://localhost:8080/sse
Cờ -N tắt bộ đệm của curl. Nếu không thấy gì cho tới khi ngắt, máy chủ của bạn đang gom thân response thay vì gửi từng đoạn — thiếu setChunked(true) hoặc có một proxy ở giữa đang đệm lại.