Hiểu thêm về Head-of-line blocking và Qpack trong HTTP2

  • Saturday 06/02/2021

Head-of-line blocking

Một trong những cải tiến HTTP/2 đem đến là khả năng multiplex nhiều HTTP request trên cùng một kết nối TCP. Điều này cho phép các ứng dụng sử dụng HTTP/2 xử lý các request đồng thời và tối ưu hơn băng thông có sẵn.

Đây thực sự là cải tiến lớn so với thế hệ trước, yêu cầu app phải tạo nhiều kết nối TCP+TLS nếu app muốn xử lý đồng thời nhiều request HTTP/1.1 (VD như khi trình duyệt cần phải tải đồng thời Javascript và CSS để hiển thị trang). Tạo nhiều kết nối mới yêu cầu phải lặp đi lặp lại việc bắt tay nhiều lần, đồng thời phải trải qua quá trình khởi động (warm-up) dẫn tới việc hiển thị trang web bị chậm. Khả năng multiplex nhiều HTTP tránh được tất cả những điều này.

Tuy nhiên, vẫn có nhược điểm. Vì nhiều request/response được truyền qua cùng một kết nối TCP, tất cả đều bị ảnh hưởng bởi việc mất gói tin (packet loss), VD: do nghẽn mạng (network congestion), kể cả khi chỉ phần dữ liệu bị mất chỉ ảnh hưởng đến một kết nối. Hiện tượng này được gọi là “head-of-line blocking”.

QUIC đã tiến một bước sâu hơn và hỗ trợ việc multiplexing sao cho: các luồng (stream) HTTP khác nhau sẽ được map với các luồng QUIC transport khác nhau, nhưng tất cả vẫn chia sẻ chung một kết nối QUIC, nên không cần phải bắt tay lại. Thêm vào đó, tuy trạng thái nghẽn mạng được chia sẻ nhưng các luồng QUIC được phân phát riêng biệt nên trong phần lớn các trường hợp việc mất gói tin của một luồng không làm ảnh hưởng đến luồng khác.

Việc này có thể giảm đi trông thấy thời gian cần đển hiển thị hoàn chỉnh trang web (với CSS, Javascript, ảnh và các thể loại media khác nhau) đặc biệt trong tình huống đường mạng hay bị nghẽn và tỉ lệ rớt mạng cao.

QPACK

Một lợi điểm khác được HTTP/2 giới thiệu là nén header (header compression (HPACK)) cho phép HTTP/2 end-point giảm lượng dữ liệu phải truyền qua mạng bằng cách loại bỏ các phần thừa trong HTTP request và response.

Cụ thể, HPACK có các bảng động (dynamic tables) chứa các header đã được gửi (hoặc được nhận) trong HTTP request (hoặc response) trước đó, cho phép các end-point tham chiếu các header trước đó với các header gặp phải ở trong request (hoặc resoponse) mới và không cần truyền lại nữa.

Các bảng động của HPACK cần phải được đồng bộ giữa bên mã hoá (phía gửi HTTP request hoặc response) và phía giải mã (nơi nhận chúng) nếu không thì phía giải mã sẽ không thể giải mã được.

Với HTTP/2 chạy trên TCP, việc đồng bộ này diễn ra rất rõ ràng vì lớp TCP đã xử lý giúp chúng ta việc truyền đi các HTTP request và response theo đúng thứ tự chúng được gửi. Khi đó, phía mã hoá có thể gửi hướng dẫn cập nhật bảng như là một phần của request (hoặc response) khiến cho việc mã hoá trở nên rất đơn giản. Nhưng đối với QUIC thì mọi chuyện lại phức tạp hơn.

QUIC có thể gửi nhiều HTTP request ( hoặc response) qua nhiều luồng đồng lập, nghĩa là nếu chỉ có một luồng, QUIC cũng sẽ cố gắng gửi theo đúng thứ tự nhưng khi nhiều luồng xuất hiện, trật tự không còn được đảm bảo nữa.

Ví dụ, nếu client gửi một HTTP request A qua luồng A, và request B qua luồng B, thì có thể xuất hiện trường hợp như sau: do việc sắp xếp lại thứ tự của gói tin mà request B được server nhận trước request A, và nếu request B được client encode và có tham chiếu đến một header từ request A thì server sẽ không thể decode được vì chưa nhận được request A ?!

Ở giao thức gQUIC, vấn đề này được giải quyết đơn giản bằng xếp thứ tự tất cả các header của HTTP request và response (chỉ header chứ không phải body) trên cùng một luồng gQUIC. Khi đó tất các header sẽ được truyền đúng thứ tự trong mọi trường hợp. Đây là một cơ chế rất đơn giản cho phép có thể sử dụng lại rất nhiều code của HTTP/2 nhưng ngược lại cũng gia tăng vấn đề head-of-line blocking, thứ mà QUIC được thiết kế để làm giảm. Do đó, nhóm IETF QUIC đã thiết kế một cách mapping mới giữa HTTP và QUIC (“HTTP/QUIC”) và một cơ chế nén header mới gọi là “QPACK”.

Trong bản draft mới nhất của phần mapping HTTP/QUIC và QPACK, mỗi trao đổi HTTP request/response sử dụng một luồng QUIC 2 chiều củả chính nó, do vậy sẽ không xuất hiện tình trạng head-of-line blocking. Thêm vào đó, để hỗ trợ QPACK, mỗi phía của kết nối sẽ tạo thêm 2 luồng QUIC một chiều, một dùng để gửi cập nhật bảng QPACK đến cho phía còn lại, một để xác nhận những cập nhật này. Theo cách này mã hoá QPACK có thể sử dụng bảng động tham chiếu chỉ sau khi nó đã được xác nhận bởi phía giải mã.

Để hiểu thêm về QUIC bạn có thể tham khảo thêm bài viết này

Với dịch vụ hosting tại P.A đã hỗ trợ https bạn có thể tham khảo các  gói hosting tại P.A tại đây