11 điểm bởi GN⁺ 2025-04-13 | 3 bình luận | Chia sẻ qua WhatsApp
  • WebSocket hữu ích cho giao tiếp thời gian thực, nhưng không phải lúc nào cũng cần; các lựa chọn thay thế dựa trên HTTP có thể đơn giản và ổn định hơn
  • Trong xử lý giao dịch, quản lý kết nối và độ phức tạp phía máy chủ, WebSocket có thể gây ra overhead quá mức
  • Tận dụng HTTP Streaming và thư viện eventkit có thể xử lý đồng bộ hóa thời gian thực và sự kiện mà không cần WebSocket

WebSocket là gì

  • WebSocket là công nghệ mở một kênh giao tiếp hai chiều liên tục giữa client và server
  • Kết nối được khởi tạo qua HTTP, nhưng sau đó việc giao tiếp diễn ra bằng một giao thức riêng
  • Thường được dùng để triển khai ứng dụng thời gian thực và hữu ích nhờ khả năng giao tiếp hai chiều

Thông điệp WebSocket không theo mô hình giao dịch

  • WebSocket không đảm bảo mối liên hệ trực tiếp giữa yêu cầu và phản hồi
  • Lệnh thay đổi trạng thái và thông điệp kết quả của chúng có thể đến xen lẫn trong cùng một luồng
  • Ví dụ, nếu một client thay đổi trạng thái và phát sinh lỗi, sẽ khó biết lỗi đó thuộc về lệnh nào
  • Cách giải quyết là đưa kèm requestId để liên kết lệnh và phản hồi, nhưng điều này làm tăng độ phức tạp và chi phí quản lý
  • Cách đơn giản hơn là dùng mô hình giao dịch qua HTTP để gửi lệnh, còn WebSocket chỉ dùng để phát thông báo thay đổi trạng thái
  • Có thể tách riêng phía gửi dùng HTTP request và phía nhận dùng WebSocket hoặc một phương thức streaming khác

Khó khăn trong quản lý vòng đời kết nối WebSocket

  • Khi dùng WebSocket, bạn phải tự xử lý việc bắt đầu, kết thúc, lỗi và kết nối lại
  • Ví dụ xử lý cơ bản trong trình duyệt bao gồm mở kết nối, nhận thông điệp, phát sinh lỗi và xử lý sự kiện đóng kết nối
  • Cần thêm logic như tự động kết nối lại, buffering thông điệp, exponential backoff, v.v.
  • Trong khi đó, HTTP có điểm bắt đầu và kết thúc rõ ràng theo từng request nên triển khai đơn giản hơn
  • Việc quản lý vòng đời phức tạp như vậy chỉ hợp lý khi bạn thực sự có lý do rõ ràng để dùng WebSocket

Độ phức tạp của mã phía máy chủ tăng lên

  • WebSocket cần xử lý yêu cầu nâng cấp HTTP, đòi hỏi thêm logic handshake
  • Cần xác thực các header đặc biệt như Sec-WebSocket-Key và trả về response header phù hợp
  • Sau khi thiết lập kết nối WebSocket, phải duy trì trạng thái nhận/gửi thông điệp liên tục, và cũng có thể phát sinh các vấn đề như xử lý partial frame
  • So với chỉ dùng HTTP, việc debug và xử lý lỗi trở nên khó hơn
  • Dù framework có trừu tượng hóa một phần quy trình, độ phức tạp nền tảng vẫn còn đó

Giải pháp thay thế: HTTP Streaming

  • HTTP vốn là giao thức hỗ trợ streaming, cho phép truyền luồng dữ liệu theo thời gian thực thay vì phải đợi toàn bộ tệp hoàn tất
  • Có thể thay thế phần chức năng phía nhận của WebSocket hiện có bằng HTTP streaming
  • Có thể dùng async generator để xử lý cập nhật trạng thái dưới dạng luồng
  • Luồng phía server
    • Cập nhật trạng thái được thực hiện trong hàm xử lý lệnh
    • Các client đã kết nối sẽ nhận được giá trị mới mỗi khi nó xuất hiện thông qua generator
    • Lệnh thay đổi trạng thái được gửi qua HTTP POST, còn luồng thời gian thực được đăng ký bằng request GET
  • Luồng phía client
    • Nhận dữ liệu thời gian thực qua Fetch API và Stream Reader
    • Giải mã văn bản rồi cập nhật UI
  • Với cấu trúc này, có thể triển khai đồng bộ hóa trạng thái thời gian thực mà không cần WebSocket

Bonus: Giới thiệu thư viện eventkit

  • eventkit là thư viện giúp dễ dàng xây dựng và quan sát các async stream
  • Tương tự RxJS, nhưng được cải thiện về quản lý side effect và được thiết kế dựa trên generator
  • Khi đẩy cập nhật trạng thái vào stream, client có thể nhận chúng theo thời gian thực
  • Có thể triển khai đơn giản ở cả server và client thông qua StreamAsyncObservable
  • Cách dùng eventkit phía server
    • Đẩy thay đổi trạng thái vào Stream, client sẽ đăng ký stream tương ứng
  • Cách dùng eventkit phía client
    • Nhận dữ liệu stream, giải mã rồi cập nhật UI
  • Cũng có sẵn kho GitHub chính thức và hướng dẫn về HTTP Streaming

GitHub: https://github.com/hntrl/eventkit

3 bình luận

 
[Bình luận này đã bị ẩn.]
 
[Bình luận này đã bị ẩn.]
 
GN⁺ 2025-04-13
Ý kiến trên Hacker News
  • Tôi không nghĩ HTTP streaming được thiết kế với kiểu mẫu này trong đầu. HTTP streaming dùng để chia dữ liệu lớn thành từng phần. Nếu dùng streaming như một cơ chế pub/sub thì có thể sẽ hối hận. Các bộ trung gian HTTP không lường trước kiểu lưu lượng này (NGINX, CloudFlare, v.v.). Mỗi khi kết nối Wi‑Fi bị ngắt, có vẻ như fetch API sẽ phát sinh lỗi do yêu cầu thất bại

    • Nhiều trường hợp không cần WebSockets. Server-Sent Events (SSE) là giải pháp đơn giản hơn. Thật tiếc vì SSE không được chú ý nhiều hơn
  • Gửi RequestID lên server để có được chu kỳ request/response không phải là điều kỳ quặc hay quá mức. Với các ứng dụng nghiêm túc, luôn đáng để có API kiểu send(message).then(res => ...)

    • Các yêu cầu upgrade gây khó hiểu. Thật khó chịu khi server WebSocket được nhúng trong server HTTP mà lại không được tích hợp với nhau
    • Thay vì tái sử dụng middleware đọc headers['authorization'] trong yêu cầu WebSocket, lại phải truy cập đối tượng connectionParams vốn giả làm header của request
    • Browser API của WebSocket dễ xử lý hơn EventSource
  • Video streaming là việc client yêu cầu các chunk theo range, chứ không phải một kết nối HTTP đơn lẻ

  • Nên dùng SSE thay vì EventKit

  • Tôi định dùng form submit HTTP truyền thống trong POC. Không cần gì khác cả

    • Kiến trúc sư lại khăng khăng rằng cần WebSocket
    • POC không cần XHR hay WebSocket. Đây là luồng mua hàng tuần tự
    • Cuối cùng lại phải cung cấp WebSocket không cần thiết
  • Vấn đề của HTTP2 là server push được thêm chồng lên giao thức sẵn có. HTTP là giao thức truyền tài nguyên, nên thêm overhead không cần thiết. Mục tiêu chính của HTTP2 là để server đẩy trước file/tài nguyên tới client nhằm giảm độ trễ vòng lặp

    • WebSockets là giao thức đơn giản hơn, được thiết kế cho giao tiếp hai chiều. Với một kết nối duy nhất, dễ kiểm soát luồng dữ liệu hơn. Việc quản lý trạng thái và khôi phục khi mất kết nối cũng thuận tiện. Xác thực và kiểm soát truy cập trở nên đơn giản hơn
  • WebSockets gửi theo datagram (packet), không phải theo stream. API WebSockets của các thư viện JavaScript không thể xử lý backpressure và cũng không thể xử lý mọi lỗi. Cần cẩn thận nếu muốn dùng như một TCP stream

  • Tôi đã hối hận sau khi triển khai WebSockets lên production. Có những vấn đề như NGINX đóng kết nối sau 4/8 giờ, trình duyệt không tự kết nối lại sau khi máy ngủ, v.v. Nếu có thể thì nên tránh WebSockets và các kết nối dài hạn

  • Có một cách nhìn quá lý tưởng về WebSockets. Mọi người có xu hướng dùng WebSockets cho các ca sử dụng streaming/thời gian thực. WebSockets làm mất đi sự đơn giản và các lợi ích của bộ công cụ HTTP. Giải pháp cho việc cập nhật streaming từ server là h2/h3 và SSE. Nếu có thể gom lô ở mức tối đa 0.5 req/s cho mỗi client thì không cần WebSockets

  • Những ai quan tâm đến HTTP streaming nên xem Braid-HTTP. Nó mở rộng HTTP một cách thanh lịch cho event streaming để cung cấp một giao thức đồng bộ trạng thái mạnh mẽ