- Trong ứng dụng web thời gian thực, việc chọn giữa Long Polling, WebSockets, SSE, WebRTC, WebTransport để truyền sự kiện giữa máy chủ và máy khách sẽ tạo ra khác biệt lớn về độ trễ, tính hai chiều, độ khó triển khai và các ràng buộc vận hành
- WebSockets cung cấp giao tiếp hai chiều qua một kết nối dài hạn duy nhất, nhưng trong vận hành thực tế thường phải dùng thêm thư viện như Socket.IO do cần xử lý phát hiện mất kết nối, tự kết nối lại và heartbeat ping-pong
- Server-Sent Events là luồng một chiều máy chủ → máy khách dựa trên HTTP nên đơn giản hơn trong triển khai và xử lý tái kết nối, nhưng API EventSource mặc định bị hạn chế trong việc gửi body POST hoặc header tùy chỉnh
- WebTransport hỗ trợ đa luồng cùng truyền tải tin cậy và không tin cậy trên nền HTTP/3 QUIC, nhưng tính đến tháng 3/2024 vẫn ở trạng thái Working Draft và chưa có hỗ trợ native trên Safari hay Node.js, nên vẫn khó xem là lựa chọn phổ thông
- Việc ứng dụng di động bị hệ điều hành dừng ở nền, giới hạn số kết nối trên mỗi domain, proxy/tường lửa doanh nghiệp, và việc mất sự kiện khi đang tái kết nối khiến ứng dụng thực tế cần thêm logic khôi phục đồng bộ và kiểm thử hạ tầng
Diễn tiến của các công nghệ giao tiếp thời gian thực giữa máy chủ và máy khách
- Trong ứng dụng web thời gian thực, khả năng để máy chủ gửi sự kiện tới máy khách đã trở thành một yêu cầu cốt lõi
- Ban đầu, Long Polling chạy trên HTTP được dùng như cách nhắn tin máy chủ-khách khả thi trong trình duyệt
- Sau đó, WebSockets xuất hiện như một cách chắc chắn hơn cho giao tiếp hai chiều
- Server-Sent Events (SSE) cung cấp giao tiếp một chiều từ máy chủ đến máy khách theo cách đơn giản hơn
- WebTransport có tiềm năng trở thành phương án hiệu quả hơn, linh hoạt hơn và mở rộng tốt hơn, nhưng hiện phạm vi hỗ trợ còn hạn chế
- WebRTC có thể được cân nhắc cho một số trường hợp ngách về sự kiện máy chủ-khách, nhưng mục đích của nó khác nên không được xem là lựa chọn chính
Long Polling
- Long Polling là cách mô phỏng giao tiếp push từ máy chủ bằng các request XHR thông thường
- Khi máy khách mở một request tới máy chủ, máy chủ sẽ giữ phản hồi lại cho đến khi có dữ liệu mới
- Sau khi gửi thông tin mới, kết nối sẽ đóng lại và máy khách lập tức bắt đầu request tiếp theo
- So với polling định kỳ truyền thống, cách này cập nhật nhanh hơn và có thể giảm lưu lượng mạng không cần thiết cũng như tải máy chủ
- Tuy vậy, nó kém hiệu quả hơn các công nghệ thời gian thực như WebSockets và có thể phát sinh độ trễ tùy thời điểm truyền dữ liệu
- Phần triển khai phía máy khách khá đơn giản, nhưng ở backend rất khó bảo đảm máy khách đang tái kết nối không bị lỡ sự kiện
WebSockets
- WebSockets tạo một kết nối dài hạn duy nhất giữa máy khách và máy chủ, đồng thời cung cấp giao tiếp song công hoàn toàn (full-duplex)
- Sau khi thiết lập kết nối, hai phía có thể gửi dữ liệu độc lập mà không cần chịu overhead của chu kỳ request-response HTTP
- Nó phù hợp với các ứng dụng cần độ trễ thấp và cập nhật thường xuyên như chat thời gian thực, game và nền tảng giao dịch tài chính
- API WebSocket cơ bản khá dễ dùng, nhưng trong môi trường production việc xử lý mất kết nối và tạo lại kết nối trở nên phức tạp
- Do khó phát hiện liệu kết nối còn dùng được hay không, người ta thường thêm heartbeat ping-and-pong
- Vì sự phức tạp này, trong nhiều trường hợp người ta dùng thư viện như Socket.IO, và Socket.IO cũng có thể cung cấp fallback sang Long Polling nếu cần
Server-Sent Events
- Server-Sent Events (SSE) là cách chuẩn để đẩy cập nhật từ máy chủ tới máy khách trên nền HTTP
- Không giống WebSockets, nó chỉ được thiết kế cho giao tiếp một chiều máy chủ → máy khách
- Nó phù hợp với các tình huống mà máy khách không cần gửi thông điệp ngược lại cho máy chủ, như news feed trực tiếp, tỷ số thể thao hoặc cập nhật thời gian thực
- Có thể xem SSE như việc giữ một kết nối HTTP mở và để backend stream từng dòng phản hồi mỗi khi có sự kiện
- Trên trình duyệt, máy khách khởi tạo một instance EventSource để nhận luồng sự kiện
- EventSource tự động kết nối lại khi bị ngắt, khác với WebSockets
- Máy chủ phải đặt header
Content-Typelàtext/event-streamvà định dạng các trường như loại sự kiện, payload dữ liệu, ID sự kiện và thời gian retry theo SSE specification
WebTransport
- WebTransport là API cho giao tiếp hiệu quả, độ trễ thấp giữa máy khách web và máy chủ
- Nó tận dụng HTTP/3 QUIC protocol để gửi dữ liệu trên nhiều stream
- Nó hỗ trợ đồng thời truyền tải tin cậy, truyền tải không tin cậy và truyền dữ liệu không theo thứ tự
- Nó có thể trở thành công cụ mạnh cho các ứng dụng cần mạng hiệu năng cao như game thời gian thực, live streaming và nền tảng cộng tác
- Tính đến tháng 3/2024, WebTransport đang ở trạng thái Working Draft và chưa được hỗ trợ rộng rãi
- Nó vẫn chưa dùng được trên Safari browser, và Node.js cũng chưa có hỗ trợ native
- Kể cả khi hỗ trợ rộng hơn, API này vẫn rất phức tạp, nên có khả năng nó sẽ được dùng thông qua thư viện xây trên WebTransport thay vì được gọi trực tiếp trong mã ứng dụng
WebRTC
- WebRTC là một dự án mã nguồn mở kiêm tiêu chuẩn API cung cấp khả năng giao tiếp thời gian thực trong trình duyệt và ứng dụng di động mà không cần plugin
- Nó hỗ trợ kết nối peer-to-peer để trao đổi âm thanh, video và dữ liệu giữa các trình duyệt
- Để đi qua NAT và tường lửa, nó sử dụng các giao thức như ICE, STUN và TURN
- WebRTC được tạo ra cho tương tác client-client, nhưng cũng có thể được dùng cho giao tiếp máy chủ-khách nếu khiến máy chủ hoạt động như một client
- Cách này chỉ phù hợp với các trường hợp ngách nên bị loại khỏi so sánh các lựa chọn chính
- Để WebRTC hoạt động, vẫn cần một signaling server, và máy chủ này lại chạy trên một trong các công nghệ như WebSockets, SSE hoặc WebTransport
- Vì vậy, WebRTC khó có thể được xem là phương án thay thế trực tiếp cho các công nghệ đó
Các ràng buộc chính theo từng công nghệ
-
Truyền dữ liệu hai chiều
- Chỉ WebSockets và WebTransport hỗ trợ nhận dữ liệu từ máy chủ và gửi dữ liệu từ máy khách trên cùng một kết nối
- Về lý thuyết Long Polling cũng làm được, nhưng để gửi dữ liệu mới trên một kết nối long-polling đang tồn tại thì phải thêm request HTTP khác nên không được khuyến nghị
- Với Long Polling, tốt hơn là gửi dữ liệu máy khách → máy chủ bằng request HTTP riêng mà không làm ảnh hưởng kết nối hiện có
- SSE không hỗ trợ gửi thêm dữ liệu về máy chủ
- API EventSource API native mặc định cũng không cho gửi dữ liệu trong HTTP body như POST ngay cả ở request ban đầu
- Dữ liệu phải được đưa vào URL parameter, nhưng credentials có thể bị lộ trong log máy chủ, proxy hoặc cache nên không tốt về mặt bảo mật
- RxDB dùng eventsource polyfill thay cho
EventSource APInative để tránh vấn đề này, và thư viện đó bổ sung các tính năng như custom HTTP header - fetch-event-source của Microsoft cho phép gửi dữ liệu trong body và dùng request
POSTthay vìGET
-
Giới hạn số kết nối trên mỗi domain
- Hầu hết trình duyệt hiện đại chỉ cho phép 6 kết nối trên mỗi domain, và giới hạn này làm giảm tính khả dụng của các cách nhắn tin ổn định từ máy chủ → máy khách nói chung
- Giới hạn 6 kết nối còn được chia sẻ giữa các tab trình duyệt, nên nếu cùng một trang được mở ở nhiều tab thì các tab phải dùng chung một pool kết nối
- RFC của HTTP/1.1 còn khuyến nghị con số thấp hơn, chỉ 2 kết nối trên mỗi máy chủ hoặc proxy
- Chính sách này hợp lý để ngăn DDoS từ phía người truy cập, nhưng có thể gây vấn đề cho các giao tiếp máy chủ-khách hợp lệ cần nhiều kết nối
- Để lách giới hạn, có thể dùng HTTP/2 hoặc HTTP/3 để trình duyệt chỉ mở một kết nối trên mỗi domain và xử lý dữ liệu bằng multiplexing
- Ngay cả với HTTP/2 và HTTP/3, thiết lập SETTINGS_MAX_CONCURRENT_STREAMS vẫn giới hạn số stream đồng thời thực tế, và mặc định ở hầu hết cấu hình là 100 concurrent streams
- Trình duyệt có thể tăng giới hạn kết nối cho một số API cụ thể như EventSource, nhưng các issue liên quan của Chromium và Firefox đều được đánh dấu là “won’t fix”
-
Giảm số lượng kết nối trong ứng dụng trình duyệt
- Trong ứng dụng trình duyệt, cần giả định rằng người dùng có thể mở ứng dụng đồng thời ở nhiều tab
- Mặc định, mỗi tab có thể mở một kết nối stream riêng tới máy chủ, nhưng trong đa số trường hợp điều đó là không cần thiết
- Có thể chỉ mở một kết nối duy nhất dù có nhiều tab, rồi chia sẻ nó giữa các tab
- RxDB dùng broadcast-channel npm package cùng LeaderElection để chỉ giữ một replication stream giữa máy chủ và máy khách
- Gói này cũng có thể được dùng độc lập trong các ứng dụng khác mà không cần RxDB
Ràng buộc vận hành từ thiết bị di động, proxy và tường lửa
- Trên các hệ điều hành di động như Android và iOS, rất khó duy trì các kết nối mở liên tục, bao gồm cả WebSockets
- Sau một khoảng thời gian không hoạt động, hệ điều hành di động có thể đưa ứng dụng xuống nền và đóng các kết nối đang mở
- Hành vi này là một phần của chiến lược quản lý tài nguyên nhằm tiết kiệm pin và tối ưu hiệu năng
- Vì vậy, khi máy chủ cần gửi dữ liệu tới máy khách, các nhà phát triển thường dùng thông báo đẩy trên di động thay vì kết nối duy trì liên tục
- Thông báo đẩy cho phép máy chủ báo dữ liệu mới cho ứng dụng và kích hoạt hành vi hay cập nhật của ứng dụng mà không cần giữ kết nối mở liên tục
- Trong môi trường doanh nghiệp, proxy và tường lửa có thể chặn các kết nối không phải HTTP, khiến việc đưa máy chủ WebSocket vào hạ tầng trở nên khó khăn
- Trong những môi trường như vậy, SSE dựa trên HTTP có thể là cách dễ tích hợp hơn với hệ thống doanh nghiệp
- Long Polling cũng là một lựa chọn vì nó chỉ dùng request HTTP thông thường
So sánh hiệu năng
- Khi so sánh WebSockets, SSE, Long Polling và WebTransport, cần xem đồng thời độ trễ, thông lượng, tải máy chủ và khả năng mở rộng
- Bản demo kiểm tra thời gian thông điệp trong realtime-web repo với triển khai máy chủ Go cho thấy hiệu năng của WebSockets, WebRTC và WebTransport là tương đương
- Vì WebTransport là công nghệ mới trên nền HTTP/3, có thể sau tháng 3/2024 sẽ còn xuất hiện thêm nhiều tối ưu hiệu năng
- WebTransport được tối ưu để giảm tiêu thụ điện năng, nhưng chỉ số này chưa được kiểm thử
-
Độ trễ
- WebSockets cho độ trễ thấp nhất nhờ giao tiếp full-duplex trên một kết nối duy trì duy nhất
- SSE cũng cho độ trễ thấp trong giao tiếp máy chủ → máy khách, nhưng nếu máy khách cần gửi thông điệp lên máy chủ thì phải dùng thêm request HTTP
- Long Polling có độ trễ cao hơn vì phải tạo kết nối HTTP mới cho mỗi lần truyền dữ liệu
- Trong Long Polling, nếu đúng lúc máy chủ muốn gửi sự kiện mà máy khách đang mở kết nối mới thì độ trễ có thể tăng mạnh
- WebTransport được kỳ vọng có độ trễ thấp tương đương WebSockets, đồng thời tận dụng multiplexing và congestion control hiệu quả hơn của HTTP/3
-
Thông lượng
- WebSockets có thể đạt thông lượng cao nhờ kết nối duy trì, nhưng vấn đề backpressure khi máy khách không xử lý kịp tốc độ gửi từ máy chủ có thể ảnh hưởng đến thông lượng
- SSE có overhead thấp hơn WebSockets nên trong các tình huống broadcast một chiều từ máy chủ → máy khách, nó có thể đạt thông lượng cao hơn
- Long Polling thường có thông lượng thấp và tiêu tốn nhiều tài nguyên máy chủ hơn do overhead từ việc liên tục mở rồi đóng kết nối
- WebTransport được kỳ vọng hỗ trợ thông lượng cao cho cả stream một chiều và hai chiều trong một kết nối duy nhất, và có thể vượt WebSockets ở những kịch bản cần nhiều stream
-
Khả năng mở rộng và tải máy chủ
- WebSockets có thể làm tăng đáng kể tải máy chủ khi phải duy trì nhiều kết nối, từ đó ảnh hưởng đến khả năng mở rộng của ứng dụng có lượng người dùng lớn
- SSE mở rộng tốt hơn trong các kịch bản chủ yếu cần cập nhật máy chủ → máy khách
- SSE dùng request HTTP thông thường mà không cần quy trình WebSocket như protocol upgrade, nên overhead kết nối thấp hơn
- Long Polling có tải máy chủ cao nhất do phải thiết lập kết nối thường xuyên, nên khả năng mở rộng thấp nhất và chỉ phù hợp làm cơ chế fallback
- WebTransport được thiết kế hướng tới khả năng mở rộng cao dựa trên hiệu quả xử lý kết nối và stream của HTTP/3, và có thể giảm tải máy chủ tốt hơn WebSockets lẫn SSE
Khuyến nghị theo trường hợp sử dụng
- SSE là lựa chọn trực quan nhất trong triển khai, dùng giao thức HTTP/S hiện có nên dễ tránh các giới hạn tường lửa doanh nghiệp và các vấn đề kỹ thuật có thể phát sinh với giao thức khác
- Nó dễ tích hợp với Node.js và các framework máy chủ khác
- Nó phù hợp với các ứng dụng cần cập nhật thường xuyên theo hướng máy chủ → máy khách như news feed, giá cổ phiếu và live event streaming
- WebSockets mạnh ở các kịch bản cần giao tiếp hai chiều liên tục
- Với các trường hợp cần tương tác liên tục như game trên trình duyệt, ứng dụng chat hoặc cập nhật thể thao trực tiếp, đây là lựa chọn chính
- WebTransport có nhiều tiềm năng nhưng hỗ trợ trong framework máy chủ chưa rộng, đồng thời thiếu tương thích với Node.js và Safari
- WebTransport phụ thuộc vào HTTP/3, trong khi hỗ trợ HTTP/3 của nhiều web server như nginx vẫn đang ở trạng thái experimental
- Đây là công nghệ hướng tương lai vì hỗ trợ cả truyền dữ liệu tin cậy và không tin cậy, nhưng với đa số trường hợp sử dụng hiện nay thì vẫn chưa phải lựa chọn khả thi
- Long Polling nhìn chung là cách làm cũ do kém hiệu quả và có overhead cao vì phải lặp lại việc thiết lập kết nối HTTP mới
- Nó có thể được dùng làm fallback ở những môi trường không hỗ trợ WebSockets hoặc SSE, nhưng nhìn chung không được khuyến nghị vì hạn chế hiệu năng
Vấn đề mất sự kiện khi đang tái kết nối
- Khi xây dựng tính năng trên bất kỳ công nghệ streaming thời gian thực nào, cần tính đến việc ngắt kết nối và tái kết nối
- Nếu máy khách đang kết nối lại hoặc ở trạng thái offline, nó có thể không nhận được các sự kiện phát sinh từ máy chủ qua stream
- Nếu máy chủ stream toàn bộ nội dung mỗi lần như giá cổ phiếu, việc lỡ sự kiện có thể không quan trọng
- Nhưng nếu backend chỉ stream các kết quả từng phần, thì bắt buộc phải xử lý sự kiện bị mất
- Việc backend phải ghi nhớ sự kiện nào đã được gửi thành công cho từng máy khách là cách làm kém khả năng mở rộng
- Tốt hơn là xử lý vấn đề này bằng logic phía máy khách
- RxDB Sync Engine dùng hai chế độ hoạt động
- checkpoint iteration mode: lặp lại việc truy vấn dữ liệu backend bằng request HTTP thông thường để máy khách bắt kịp cho tới khi đồng bộ lại
- event observation mode: dùng các cập nhật từ stream thời gian thực để giữ máy khách ở trạng thái đồng bộ
- Khi kết nối của máy khách bị ngắt hoặc phát sinh lỗi, replication sẽ tạm chuyển về checkpoint iteration mode để đồng bộ lại cho đến khi trở về cùng trạng thái với máy chủ
- Cách làm này bù lại các sự kiện bị mất và giúp máy khách luôn đồng bộ chính xác với máy chủ
Những điều cần xác minh trong hạ tầng doanh nghiệp
- Trong hạ tầng doanh nghiệp, các công nghệ streaming nói chung đều có thể gặp vấn đề
- Proxy và tường lửa có thể chặn lưu lượng hoặc vô tình làm hỏng request và response
- Vì vậy, khi triển khai ứng dụng thời gian thực trong những môi trường này, trước hết cần kiểm thử xem công nghệ đã chọn có thực sự hoạt động trong hạ tầng đó hay không
1 bình luận
Các ý kiến trên Hacker News
Tôi luôn có thiện cảm với Server-Sent Events. Nó đơn giản, dễ dùng/dễ triển khai
WebSocket trở nên khá phức tạp để mở rộng khi mức sử dụng vượt qua một ngưỡng nào đó
https://crbug.com/275955
Tôi tự hỏi vì sao họ không làm nó thành response streaming dạng multipart. Dạng này cũng hỗ trợ metadata và là một định dạng được triển khai rất phổ biến
Có thêm vài nhược điểm cần biết
WebSocket không có kiểm soát luồng (backpressure) và multiplexing, nên nếu cần thì bạn phải tự làm hoặc dùng thứ như RSocket. SSE cũng không thể gửi trực tiếp dữ liệu nhị phân, nên cần mã hóa kiểu base64
WebTransport xử lý các vấn đề này và cũng giải quyết HOL blocking, nhưng tôi lo nó sẽ gặp vấn đề giống chuyển đổi Python 2→3 hay IPv6: mọi người dễ tiếp tục dùng phiên bản cũ và cảm thấy lợi ích nâng cấp không đáng kể
Chừng nào trình duyệt còn tiếp tục hoạt động bằng TCP, một số mạng có thể chặn hẳn UDP, và do đó chặn HTTP/3/WebTransport
Nỗi lo rằng việc chuyển sang WebTransport có thể chậm cũng là điều từng có thể nói y như vậy về vận chuyển TLS, HTTP/3 và XHR trước đây. Vì cấu trúc thị trường bị chi phối bởi vài engine trình duyệt lớn, việc triển khai tính năng trình duyệt và giao thức mới tương đối dễ
Lập luận rằng vì TCP dùng được nên một số mạng sẽ chặn UDP cũng giống như nói rằng vì HTTP 1.1 không TLS dùng được nên HTTP/2 và TLS cũng sẽ tiếp tục bị chặn. Không hoàn toàn sai, nhưng nhìn vào mức độ chấp nhận rộng rãi của HTTP/2 và đặc biệt là TLS thì có vẻ đây không phải vấn đề lớn như tưởng tượng
Một văn phòng nhỏ hoặc môi trường doanh nghiệp phản địa đàng như trong phim có thể đóng nó lại, nhưng tôi không hiểu việc một số mạng có thể cấm UDP thì liên quan lớn đến vậy ở điểm nào. Một số mạng còn chặn cả google.com hay wikipedia.com, nhưng điều đó không khiến các dịch vụ ấy thất bại
Phần giải thích về WebRTC trong bài không chính xác. WebRTC client/server vẫn có thể làm mà không cần “máy chủ signaling” riêng; server chỉ cần đảm nhiệm signaling là được
Chỉ cần thêm vài lượt round-trip, chứ không cần máy chủ riêng. Data channel của WebRTC hoạt động khá tốt như một giải pháp thay thế WebSocket hoặc SSE, đặc biệt khi muốn tránh HOL blocking. Cũng có nhiều thư viện như Pion hay str0m gần như làm hộ mọi việc
Nói API WebTransport phức tạp cũng có vẻ phóng đại. Nếu không cần tính năng nâng cao thì cứ bỏ qua; nếu muốn dùng giống WebSocket thì chỉ cần mở một stream hai chiều là gần như xong. Muốn tránh HOL blocking thì mở một stream cho mỗi message. Phức tạp hơn một chút, nhưng chưa đến mức nhất thiết phải có thư viện, và GitHub Copilot rất có thể cũng viết code giúp được. Tuy nhiên WebTransport vẫn đang trong quá trình trưởng thành nên chưa có nhiều thư viện server, và vẫn đang chờ Safari hỗ trợ
Thông thường máy chủ signaling được triển khai bằng WebSocket. Nếu không phải bạn đang đề xuất bootstrap phi tập trung cho các client hiện có, thì không thể tự triển khai bằng chính WebRTC
Nếu đang xây cho khách hàng có hạ tầng IT “doanh nghiệp” và “bảo mật” truyền thống, tốt hơn là thêm nút refresh rồi kết thúc ở đó
Theo kinh nghiệm của tôi trong các môi trường như vậy, thứ liên tục thất bại và cũng không thể sửa vì các quy trình vô tận chính là nỗ lực xây tính năng thời gian thực cho những khách hàng kiểu này
WebSocket và SSE trở thành vấn đề đau đầu lớn về quản lý khi mở rộng quy mô. Đặc biệt ở backend cần khả năng quan sát riêng, và nếu không triển khai thật cẩn thận trên thiết bị di động thì việc debug frontend sẽ thành ác mộng
Thiết bị thường tắt hoặc làm chậm mạng để tiết kiệm pin, nhất là khi không thực hiện I/O một cách tường minh qua API chuyên dụng
Tạo kết nối mới là thao tác tốn kém, và server phải lưu trạng thái ở đâu đó. Nếu tầng lưu trạng thái này gặp vấn đề, client sẽ liên tục thử lại, bị timeout và mắc kẹt mãi trong các thao tác tốn kém. Đây cũng không phải cách dễ kiểm soát thông lượng trong khi tạo tải lên cơ sở dữ liệu một cách từ từ
Về độ tin cậy, theo kinh nghiệm thì long polling là tốt nhất. Ngay cả khi luồng dựa trên sự kiện thực sự quan trọng, frontend nên long poll tới backend tầng 1, rồi tầng 1 đó subscribe tới backend tầng 2 bằng WebSocket; cấu trúc 2 tầng như vậy tốt hơn và kiểm soát độ tin cậy tốt hơn nhiều
SSE hỗ trợ tự động kết nối lại và còn kèm ID đã thấy gần nhất để server có thể tiếp tục liền mạch
Bài viết không nhắc tới, nhưng short polling cũng liên quan. Dù không phải cách gửi thông điệp từ server tới client, nó vẫn hữu ích khi không có lựa chọn nào khác, chẳng hạn trên shared hosting
Theo kinh nghiệm của tôi, ngay cả khi khoảng thời gian polling dài, ví dụ 20 giây, nếu mỗi phản hồi kèm theo danh sách thông điệp thì vẫn hoạt động khá tốt. Khi người dùng bấm nút, client gửi yêu cầu tới server, server phản hồi kèm dữ liệu và danh sách thông điệp mới nhất để client được cập nhật
Đến giờ tôi vẫn không hiểu vì sao WebSocket và SSE không hỗ trợ gửi các header như Authorization trong yêu cầu ban đầu. Chúng đẩy toàn bộ phần xác thực dịch vụ thời gian thực cho người triển khai
Có thể trong spec có cách hay, nhưng tôi đã thấy quá nhiều cách tiếp cận khác nhau đến mức giờ gần như có thể nói là không có
Không chỉ xử lý header tùy chỉnh, nó còn hỗ trợ mọi method yêu cầu (POST, PATCH, v.v.), kèm request body, subscribe các event có tên, và đặt last event ID ban đầu. Cũng có thể dùng như async iterator
Tôi thích sự đơn giản của Server-Sent Events, nhưng API
EventSourcetrông như được triển khai vội rồi cứ thế để lại[1]: https://github.com/eventsource/eventsource
Có thể là suy nghĩ ngây thơ, nhưng nếu giả định dùng HTTP/2 trở lên, thì kết hợp EventSource với
fetch()để gửi thông điệp có vẻ cũng ổn không kém các giao thức khác dùng một kết nối TCP duy nhất. HTTP/3 dùng UDP nên còn tốt hơnTiền đề là chỉ cần duy trì kết nối khi tab ở foreground. Tôi tò mò khi thực sự thử cách này thì đã gặp vấn đề gì
https://www.npmjs.com/package/@microsoft/fetch-event-source
Thay vì làm một thứ hoàn toàn khác, tôi đã tò mò liệu có thể đẩy SSE đi xa hơn với độ trễ, mức dùng bộ nhớ và tài nguyên CPU thấp hơn không
Đọc những bài như thế này khá thú vị. Cuối thập niên 90, tôi thiết kế một hệ thống đấu giá trực tuyến, và khi đó hoàn toàn không có yêu cầu XHR
Cập nhật thời gian thực đều được xử lý bằng server-push/HTTP streaming. Khi ấy không dễ xử lý tất cả các kết nối mở, nhưng với kiến trúc phù hợp thì vẫn có thể đạt quy mô chấp nhận được
Lợi ích của HTTP/2 hay HTTP/3 rất tuyệt, nhưng cũng cần biết rằng có những thứ có thể tận dụng ngay trên HTTP 1.1 vốn được hỗ trợ gần như ở khắp nơi
Có hơi nhớ long polling. So với các công nghệ mới thì nó thật sự đơn giản. Ngay cả khi cho rằng WebRTC là tốt nhất, tôi vẫn cảm thấy vậy
Thay vào đó, nó lại chờ dữ liệu rồi gửi thêm phản hồi trên cùng một stream
Phần networking của Second Life dùng HTTPS long polling cho “kênh sự kiện”, và server gửi các thông điệp sự kiện tới client qua kênh đó. Phần lớn thông điệp đi qua UDP, nhưng các thông điệp cần mã hóa hoặc có kích thước lớn thì đi qua kênh sự kiện HTTPS/TCP
Ở phía client, client C++ dùng
libcurl, nhưng thiết lập timeout mặc định không phù hợp với long polling.libcurlngắt kết nối và tạo request mới, kết quả là có thể mất hoặc trùng lặp thông điệpỞ phía server, Apache đứng trước server mô phỏng thực tế để lọc các nỗ lực kết nối không liên quan, nhưng Apache cũng có timeout riêng nên nó ngắt kết nối và khiến client thử lại
Họ cố ngăn mất mát bằng số thứ tự thông điệp, nhưng server Second Life bỏ qua số thứ tự mà client gửi lại để xác nhận. Một số server tương thích của Open Simulator thậm chí còn bỏ qua số thứ tự tuần tự
Kết quả là một hệ thống dựa trên HTTPS có thể làm mất hoặc nhân đôi các thông điệp vốn cần phải đáng tin cậy. Nếu mất một số thông điệp, hoạt động của người dùng trong game sẽ bị khựng lại
Những người thiết kế thứ này đã rời đi từ lâu, và các nhân viên hiện tại không biết mớ hỗn độn này nghiêm trọng đến mức nào. Người dùng bên ngoài đã phải tìm ra vấn đề và ghi lại tài liệu, còn nhân viên công ty đã cố sửa trong nhiều tháng. Có vẻ việc sửa đủ khó đến mức hiện tại họ đang có xu hướng trì hoãn
Vì vậy long polling không “đơn giản đến mức ngốc nghếch”. Cách đúng có lẽ là gửi thông điệp keep-alive đủ thường xuyên để các lớp TCP và HTTPS không bị timeout. Như vậy Apache và
libcurlsẽ ở trong đường chạy mà chúng hoạt động tốt