真正「搞」懂HTTP協定14之HTTP3

2023-02-13 12:01:12

  我們前一篇學習了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是如何在「安全」這條路上走向完善的。