- Điều quan trọng trong video trực tiếp và hội thoại trên Internet không phải bản thân việc “truyền không tin cậy”, mà là đẩy dữ liệu cũ ra và chuyển dữ liệu mới nhất đúng lúc
- Nếu router không loại bỏ các gói bị tắc nghẽn mà để chúng xếp hàng quá lâu trong queue, sẽ xảy ra bufferbloat, gây ra độ trễ còn tệ hơn cả buffering đối với dịch vụ thời gian thực
- Nếu tự xây giao thức trực tiếp trên UDP, bạn sẽ phải triển khai lại truyền lại, điều khiển tắc nghẽn, mã hóa, ước lượng RTT, xác thực đường đi, điều khiển luồng... nên dùng thư viện QUIC sẽ an toàn hơn
- Chỉ với QUIC, cũng có thể tạo ra tính kịp thời mà không cần datagram bằng cách kết hợp điều khiển tắc nghẽn dựa trên độ trễ, phân tách stream độc lập và gán mức ưu tiên
- Dù các tiêu chuẩn QUIC, WebTransport và MoQ đều đang bổ sung hỗ trợ datagram, kết luận là tốt hơn nên đi theo luồng Media over QUIC thay vì lại chồng một giao thức video mới lên trên UDP
Mục tiêu không phải là “tính không ổn định” mà là tính kịp thời
- Việc chọn TCP hay UDP thường được giải thích là giữa “truyền tin cậy” và “truyền không tin cậy”, nhưng điều ứng dụng thực sự cần không phải là bản thân tính không ổn định
- Trong video trực tiếp hay hội thoại trên Internet, việc dữ liệu mới nhất đến trước quan trọng hơn là nhận đủ toàn bộ dữ liệu cũ
- Trong hội thoại thời gian thực, cần tránh tình huống hiện vòng xoay buffering trên khuôn mặt hoặc nghe thấy âm thanh từ 5 giây trước
- Ngành video trực tiếp và ngành game thường dùng UDP datagram thay cho TCP stream để đạt được tính kịp thời như vậy
Datagram và hàng đợi mạng
- Datagram là một phong bì chứa các bit 0 và 1 được gửi từ địa chỉ nguồn đến địa chỉ đích, và thông thường 1200 byte được xem là kích thước an toàn
- Datagram có thể bị mất một cách âm thầm, và cũng có thể đến không đúng thứ tự
- Ở tầng vật lý, dữ liệu được biến thành tín hiệu tương tự để đi qua môi trường truyền dẫn; trong quá trình đó có thể xảy ra tuần tự hóa, giải tuần tự, buffering, xếp hàng, truyền lại, loại bỏ, hỏng dữ liệu, trì hoãn, sắp xếp lại, trùng lặp và mất mát
- Khi quá nhiều dữ liệu đi vào mạng, router sẽ loại bỏ dữ liệu ở ranh giới gói tin chứ không phải ở mức bit ngẫu nhiên
Bufferbloat và điều khiển tắc nghẽn
- Nếu router không loại bỏ gói ngay mà tích chúng lại trong queue, bufferbloat sẽ xảy ra
- bufferbloat có thể làm chậm mọi gói tin đến vài giây, tạo ra tình huống tệ nhất cho truyền tải thời gian thực
- Để tránh queueing, cần ước lượng mức xếp hàng của router thông qua phản hồi về thời điểm gói đến, rồi bên gửi phải giảm lượng truyền để làm trống queue
- Lĩnh vực này thuộc về điều khiển tắc nghẽn, và cách gửi gói ở tốc độ không giới hạn có thể dẫn đến thảm họa
Những chức năng phải gánh khi tự xây trên UDP
- Nếu tự dùng UDP để xây giao thức truyền tải, tối thiểu cần các chức năng sau
- Muốn tạo ra một giao thức tốt hơn, còn cần thêm các chức năng sau
- Để tăng độ hoàn thiện, còn phải tính đến môi trường vận hành và triển khai
- Các giao thức video trực tiếp như WebRTC, SRT, Sye, RIST là những ví dụ được xây trên UDP
- Điều này dẫn đến nhận định rằng dùng thư viện QUIC sẽ tốt hơn là tự làm một giao thức mới
Tạo tính kịp thời bằng QUIC stream
- Có thể tóm tắt cách đạt được tính kịp thời trong QUIC thành ba điểm
- Tránh phình bộ đệm: điều khiển tắc nghẽn dựa trên độ trễ như BBR sẽ phát hiện queueing và giảm lượng truyền
- transport-wide-cc của WebRTC có thể được xem là một ví dụ về cách làm tốt hơn
- Phân tách stream: byte trong mỗi stream có thứ tự và được truyền tin cậy; mỗi stream có thể là một đơn vị nguyên tử như khung hình video, bản cập nhật game, tin nhắn chat hay JSON blob
- Gán ưu tiên cho stream: các stream là độc lập nên có thể đến không theo thứ tự, và có thể chỉ thị cho QUIC stack chuyển các stream quan trọng trước
- Các stream ưu tiên thấp có thể bị đói tài nguyên, và có thể đóng chúng để tránh lãng phí băng thông
- Đây là cốt lõi của Media over QUIC
- Tính chất fire-and-forget của datagram chỉ phù hợp khi thực sự cần độ trễ thời gian thực; ngoài ra có thể dùng QUIC stream
Những đánh đổi và ngoại lệ quanh datagram
- QUIC và các tiêu chuẩn liên quan cũng bao gồm hỗ trợ datagram
- QUIC cung cấp hỗ trợ datagram thông qua extension
- WebTransport yêu cầu hỗ trợ datagram
- Phiên bản MoQ mới nhất đã thêm hỗ trợ datagram
- Phiên bản MoQ tiếp theo dự kiến sẽ yêu cầu hỗ trợ datagram
- Hỗ trợ datagram có thể được đưa vào vì việc triển khai khá đơn giản và cho phép thử nghiệm
- OPUS có tích hợp hỗ trợ FEC, nên đây là ví dụ cho việc MoQ hỗ trợ gửi mỗi “frame” âm thanh dưới dạng datagram
- Giao thức cũ như DNS có thể được xem là ngoại lệ, nhưng với thiết kế mới thì tốt hơn nên đi theo hướng như DNS over HTTPS
- Kết luận là thay vì lại xây một giao thức video mới trên UDP, hãy tham gia Media over QUIC
1 bình luận
Ý kiến trên Hacker News
Ví dụ, trong môi trường như NB-IoT, nơi độ trễ khứ hồi trong trường hợp xấu nhất là 10 giây, thời gian khứ hồi bị lãng phí cho handshake và dò MTU; TCP vẫn cố truyền cả dữ liệu không còn hữu ích nữa; và khi vùng phủ sóng xấu đi làm độ trễ tăng, TCP xem đó là mất gói do tắc nghẽn và giảm băng thông
Ngoài ra, load balancer hoặc middlebox có thể ngắt kết nối vì nghĩ rằng “không có phản hồi trong 4 giây thì chắc nó biến mất rồi”, và việc TCP chia gói mà không xét đến cấu trúc dữ liệu, khiến không thể diễn giải cho đến khi nhận đủ toàn bộ, cũng là một điểm đáng tiếc
Trong một số trường hợp điều này hữu ích, nhưng có nhiều trường hợp vẫn có thể tận dụng gói n+1 dù gói n chưa có
Khi truyền tệp lớn, có thể dùng erasure coding để tự xử lý mất 5% gói, hoặc dùng fountain code để gửi cho đến khi bên nhận nói “đã nhận đủ”
Fountain code là cách các tàu thăm dò không gian sâu gửi dữ liệu, và độ trễ tới Sao Mộc hay Sao Hỏa là khá nghiêm trọng
Phải hoàn tất TCP handshake rồi mới có thể bắt đầu TLS handshake, nhưng QUIC có hỗ trợ ở cấp giao thức để xử lý đàm phán TLS ngay trong handshake ban đầu
Việc có thể kết hợp lỏng lẻo giao thức mạng và mã hóa trông có vẻ thanh nhã hơn, nhưng trong một thế giới mà gần như mọi truyền tải đều được mã hóa, lợi ích thực tế của việc giảm một vòng khứ hồi cho mỗi kết nối có vẻ lớn hơn
Bên R&D đã làm một hệ thống mới dùng QUIC và giải quyết được phần lớn vấn đề dữ liệu đến sai thứ tự, nhưng các cảm biến bên thứ ba cần được hỗ trợ trực tiếp mà không qua adapter lại chỉ dùng được UDP, nên cuối cùng vẫn dùng UDP datagram cho mọi thứ
Cách diễn đạt phổ biến hơn và tốt hơn là best-effort: UDP cố gắng hết mức để chuyển datagram, chỉ là datagram có thể bị bỏ
Điều đó không có nghĩa UDP về bản chất là không đáng tin cậy
https://en.wikipedia.org/wiki/Best-effort_delivery
Trên thực tế nó không có nghĩa là cố gắng đến cùng để gửi thông điệp từ A đến B, mà gần với “cũng đã cố rồi” hơn
Nếu router trên đường đi bị tắc nghẽn, hoặc do link flap tạo ra một blackhole khoảng 50ms trước khi định tuyến lại nhanh, thì cũng thành “ừ thì đã thử rồi”
Ngược lại, truyền tải đáng tin cậy của TCP sẽ thử lại nhiều lần và cung cấp cho ứng dụng một luồng dữ liệu đúng thứ tự
reliable/unreliable cũng có thể là thuật ngữ tệ, nhưng khó nói best-effort là tốt hơn
Các hệ thống không đáng tin cậy thì khoảng 95% là rất tốt và cũng tốt cho thông lượng thô, nhưng 5% cuối cùng thường tạo ra khác biệt rất lớn
“Nỗ lực” thường hàm ý một mức độ bền bỉ nào đó trước khó khăn, nhưng việc vứt gói khi gặp vấn đề tài nguyên thì khó gọi là nỗ lực, càng không phải best effort
“best efforts” trong ngôn ngữ pháp lý/kinh doanh yếu hơn một cam kết chắc chắn, nhưng không phải là công khai buông tay; cách dùng trong networking thì khá khác
Riêng checksum của UDP và TCP cũng không bảo đảm tốt tính toàn vẹn khi datagram được chuyển tới, chỉ nhỉnh hơn phần cứng một chút
Tuy nhiên best-effort lại tạo cảm giác rằng có nỗ lực nào đó để bảo đảm chuyển phát, trong khi thực tế thì những gói trông có vẻ bất thường hoặc xui xẻo gặp buffer đầy sẽ bị vứt luôn
Tôi thích lossy hơn, nhưng đây là vấn đề đặt tên thuộc loại “chỉ có hai bài toán khó”
Nếu gói cần phải đến nơi thì dùng TCP, còn nếu không quá quan trọng thì dùng UDP
Dù là giải thích đơn giản hóa, best effort là một thuật ngữ ngớ ngẩn, và trong đó chẳng có nỗ lực nào cả
Kiểm soát tắc nghẽn rõ ràng là cần thiết, nhưng ngoài chuyện đó thì có rất nhiều điều đáng nghi
Nếu là một thế giới datagram-first, chúng ta đã có thể ghép nhiều data link với hiệu quả rất cao, hoặc roaming qua các ranh giới mạng mà không cần ngắt kết nối
Nhiều ứng dụng có thể xử lý frame đến sai thứ tự mà không tốn thêm chi phí, và nếu viết theo mô hình UDP thì có thể nhanh hơn nhiều
Trên thực tế, chuyển sang phương thức truyền tải kém tin cậy hơn không tự động khiến phần mềm trở nên đáng tin cậy hơn hay hiệu quả hơn
Ngược lại, số chế độ lỗi và độ phức tạp mà nhóm phải xử lý tăng lên đáng kể
Tuy nhiên, nếu đâu đó trong kết nối có một nút thắt hẹp, thì việc tạo ra rất nhiều gói tin rồi dù sao cũng sẽ bị loại bỏ ở nút thắt đó có vẻ không có nhiều ý nghĩa
Website, âm thanh, video nhìn chung không hợp lắm với các frame bị đảo thứ tự, và phần lớn mọi người không muốn âm thanh hay video bị giật
Một số video game có thể bỏ qua các gói bị thiếu, nhưng những trường hợp đó vốn đã được viết bằng UDP rồi
Vì người ta cho rằng những gói đó sẽ không được truyền lại, nên đây là cách hiệu quả để giảm lưu lượng dư thừa
Giờ đã có các giao thức trên UDP truyền lại một cách tích cực, tôi tò mò điều này đã thay đổi tình hình ra sao
Tôi cũng nhớ vài năm trước QUIC từng gặp vấn đề truyền lại so với HTTP/1·HTTP/2 vì lý do này
Nếu tìm được một cụm từ tiêu biểu hơn trong bài thì có thể đổi lại
Điều này theo hướng dẫn tiêu đề của HN: “dùng tiêu đề gốc, trừ khi nó gây hiểu lầm hoặc mang tính câu kéo”: https://news.ycombinator.com/newsguidelines.html
Tùy ứng dụng mà điều đó hợp lý
Ví dụ trong game multiplayer thời gian thực, nếu xử lý bị tụt lại, các mục bị trễ không còn quan trọng nữa vì trạng thái game đã thay đổi rồi
Các ứng dụng giao dịch tốc độ cao trong một số tình huống cũng chỉ cần dữ liệu thị trường mới nhất, chứ không phải chuyện của 100ms trước
Bài viết cũng nêu game và video trực tiếp làm ví dụ về nơi UDP phù hợp
Cấu trúc là ban đầu đưa ra “quan niệm phổ biến” rồi phản bác nó
Ví dụ gồm khám phá cục bộ như DHCP, SLAAC, UPnP, mDNS, tinc, BitTorrent; broadcast như streaming mạng cục bộ; và đóng gói như WireGuard, IPSec, OpenVPN, VLAN
Việc truyền lại, thậm chí chỉ cần buffering để sắp xếp lại thứ tự, cũng làm tăng độ trễ, nên tốt hơn là chấp nhận mất mát bằng sửa lỗi hoặc che giấu mất gói
UDP và TCP có hành vi và các điểm đánh đổi khác nhau, nên tất cả chỉ là cần hiểu trước khi chọn cho phù hợp với use case
Không cần kiểu gác cổng “tuyệt đối đừng làm X”
Chỉ nhìn tiêu đề cũng khá rõ rằng bài không nhằm gác cổng cấm dùng UDP
Thực tế ở cuối bài, tác giả đề xuất dùng QUIC, vốn dựa trên UDP
Tất nhiên bạn sẽ phải tự lo nhiều chi tiết hơn rất nhiều
Thêm nữa, đây cũng là cách tốt để học các khía cạnh cấp thấp của mạng