真正“搞”懂HTTP協議14之HTTP3

Zaking發表於2023-02-13

  我們前一篇學習了HTTP/2,相比於HTTP/1,HTTP/2在效能上有了大幅的改進,但是HTTP/2因為底層還是基於TCP協議的,雖然HTTP/2在應用層引入了流的概念,利用多路複用解決了隊頭阻塞的問題,但是在TCP中隊頭阻塞的問題仍舊存在。

  又由於TCP協議的僵化、TCP的慢啟動,為了確保連線建立而產生的延遲問題等頑固問題。HTTP/2雖然在效能上達到了極致,但是還是改變不了底層TCP的效能影響。畢竟應用層。得,死路一條。那怎麼辦呢?

一、QUIC協議

  既然不能用TCP,那就用UDP好啦。我們來看張圖,把我們學過的協議的棧層都羅列一下,方便對比:

   HTTPS的SSL和TLS我們在下一篇就開始聊,先不管。我們看HTTP/1、HTTPS、HTTP/2,和HTTP3在傳輸層有啥區別?HTTP/3用的是UDP。因為UDP是無序的,包與包之間沒有依賴關係,就像HTTP/2的多路複用一樣,從根本上解決了TCP的隊頭阻塞。

  但是,你肯定知道,UDP是一個簡單的,不可靠的協議,只是對IP協議的一層很薄的包裝,和TCP相比,它的實際應用很少。不過正是因為它簡單,不需要建連和斷連,通訊成本低,也就非常靈活、高效,“可塑性”很強。

  所以,QUIC 就選定了 UDP,在它之上把 TCP 的那一套連線管理、擁塞視窗、流量控制等“搬”了過來,“去其糟粕,取其精華”,打造出了一個全新的可靠傳輸協議,可以認為是“新時代的 TCP”。

  QUIC 最早是由 Google 發明的,被稱為 gQUIC。而當前正在由 IETF 標準化的 QUIC 被稱為 iQUIC。兩者的差異非常大。gQUIC 混合了 UDP、TLS、HTTP,是一個應用層的協議。而 IETF 則對 gQUIC 做了“清理”,把應用部分分離出來,形成了 HTTP/3,原來的 UDP 部分“下放”到了傳輸層,所以 iQUIC 有時候也叫“QUIC-transport”。接下來要說的 QUIC 都是指 iQUIC,要記住,它與早期的 gQUIC 不同,是一個傳輸層的協議,和 TCP 是平級的。

  聊到這裡,我又想起了之前說過的話,如果一層不行,那就再加一層~

特點

  瞭解了QUIC的基本概念,我們再來看看QUIC的特點,這樣的特點給我們帶來了哪些好處。

  前面說過,QUIC是基於UDP的,而UDP是無連線的,根本就不需要握手和揮手,所以天生比TCP快很多。

  就像 TCP 在 IP 的基礎上實現了可靠傳輸一樣,QUIC 也基於 UDP 實現了可靠傳輸,保證資料一定能夠抵達目的地。它還引入了類似 HTTP/2 的“流”和“多路複用”,單個“流”是有序的,可能會因為丟包而阻塞,但其他“流”不會受到影響。

  並且,QUIC全面使用加密通訊,這樣可以很好的抵禦篡改和協議僵化。

  另外,QUIC並不是建立在TLS之上的,而是內部包含了TLS。它使用自己的幀“接管”了 TLS 裡的“記錄”,握手訊息、警報訊息都不使用 TLS 記錄,直接封裝成 QUIC 的幀傳送,省掉了一次開銷。

細節

  QUIC的內容很多,我們只簡單的聊一聊兩個內部的關鍵知識點。

  QUIC基本資料傳輸單位是包(packet)和幀(frame),一個包由多個幀組成,包面向的是”連線“,幀面向的是”流“。

  QUIC 使用不透明的“連線 ID”來標記通訊的兩個端點,客戶端和伺服器可以自行選擇一組 ID 來標記自己,這樣就解除了 TCP 裡連線對“IP 地址 + 埠”(即常說的四元組)的強繫結,支援“連線遷移”(Connection Migration)。換句話說,當IP變化的時候,QUIC連線裡的兩端ID不會變,邏輯上連線沒有中斷,所以無需像TCP一樣再重新連線,節省了一定的效能,消除了連線成本,實現連線的無縫遷移。

二、HTTP/3

  瞭解了QUIC後,再來學習HTTP/3就要容易很多了。

  因為 QUIC 本身就已經支援了加密、流和多路複用,所以 HTTP/3 的工作減輕了很多,把流控制都交給 QUIC 去做。呼叫的不再是 TLS 的安全介面,也不是 Socket API,而是專門的 QUIC 函式。不過這個“QUIC 函式”還沒有形成標準,必須要繫結到某一個具體的實現庫。

  HTTP/3 裡仍然使用流來傳送“請求 - 響應”,但它自身不需要像 HTTP/2 那樣再去定義流,而是直接使用 QUIC 的流,相當於做了一個“概念對映”。

  HTTP/3中的頭部壓縮演算法也升級成了“QPACK”,使用方式上也做了改變。雖然也分成靜態表和動態表,但在流上傳送 HEADERS 幀時不能更新欄位,只能引用,索引表的更新需要在專門的單向流上傳送指令來管理,解決了 HPACK 的“隊頭阻塞”問題。另外,QPACK 的字典也做了最佳化,靜態表由之前的 61 個增加到了 98 個,而且序號從 0 開始,也就是說“:authority”的編號是 0。

  還有一個有趣的點是,HTTP/3 沒有指定預設的埠號,也就是說不一定非要在 UDP 的 80 或者 443 上提供 HTTP/3 服務。那麼,該怎麼“發現”HTTP/3 呢?

  這就要用到 HTTP/2 裡的“擴充套件幀”了。瀏覽器需要先用 HTTP/2 協議連線伺服器,然後伺服器可以在啟動 HTTP/2 連線後傳送一個“Alt-Svc”幀,包含一個“h3=host:port”的字串,告訴瀏覽器在另一個端點上提供等價的 HTTP/3 服務。瀏覽器收到“Alt-Svc”幀,會使用 QUIC 非同步連線指定的埠,如果連線成功,就會斷開 HTTP/2 連線,改用新的 HTTP/3 收發資料。

三、示例

  如果你想嘗試HTTP/3的例子,我們可以在這個網址玩一下,https://quic.nginx.org/。如果不行的話,就需要多重新整理幾下,如果還是不行,那就需要開啟Chrome瀏覽器在位址列輸入“chrome://flags”,開啟設定頁面,然後搜尋“QUIC”,找到啟用 QUIC 的選項,把它改為“Enabled”。就像這樣:

  然後,再去重新整理幾下,運氣好的話,就可以看到這樣的Network了:

    如果你發現不管你怎麼重新整理都不行的話,試試把你的VPN關了應該會好的~

  這是整個H3的報文:

   是不是有那麼一點點陌生又熟悉?

四、小結

  本篇,我們簡單的聊了聊HTTP/3,聊了聊QUIC是什麼,它是怎麼解決之前HTTP為之苦惱的隊頭阻塞的問題。以及HTTP/3的協議棧相比於HTTP/2、HTTP/1有什麼區別,多了哪些內容等等。HTTP/3可以說是集大成之作,把之前所有版本的HTTP協議的精華摘取,去其糟粕,形成了現在幾乎完美的HTTP/3協議。

  那麼關於“效能”部分,我們就到此為止了,後面我們會花幾篇文章的時間,來學一學HTTPS,也就是HTTP是如何在“安全”這條路上走向完善的。

相關文章