淺談 HTTP/3 與 QUIC

Jalex Chang
Jalex’s Murmur
Published in
16 min readAug 30, 2019

前言

在 2019 八月中旬時,小弟很榮幸地,有機會在 COSCUP2019 的 SDN x Cloud Native x Golang 議軌中擔任講者。當時的題目為 HTTP/3 Leaks,主要介紹下個世代的 HTTP 協定 — HTTP/3與其所使用的傳輸層協定(Transport Protocol) — QUIC。而似乎是場地限制的關係,本場演講並沒有影片釋出。因此,希望可以藉由這篇文章,簡單地介紹 HTTP/3 與 QUIC,讓更多人了解新協定的特徵功能與運作流程。

值得注意的是,在技術分享與撰寫本文的當下, HTTP/3 與 QUIC 還未確立最終的版本。因此本文的所描述的標準協定是基於時空背景下最新的草稿 — draft-ietf-quic-http-22draft-ietf-quic-transport-22,其內容可能與最終版本有所出入。

簡介

本文章的目的是介紹 HTTP/3 與其底層的 QUIC,其涵蓋的議題包括:

  • 為什麼需要新的 HTTP 協定?
  • 為什麼需要新的傳輸協定 (transport protocol)?
  • QUIC 的特徵
  • HTTP/3 與 HTTP/2 的比較
  • HTTP/3 常見的批評與顧慮

HTTP/2 的缺點

當我們談論到為什麼需要新的 HTTP 協定時,必須先了解目前的 HTTP 版本 (HTTP/2) 遇到了哪些問題。而大致上可以概括成兩點:

  • 傳輸層的 Head-of-Line Blocking
  • HTTP 連線建立過程冗長

傳輸層的 Head-of-Line Blocking

Head-of-line blocking (HOL blocking) 是一種效能受限的現象,指得是當前處理的請求受阻而導致整個序列中的請求都受阻的情況。就像排隊結帳一樣,如果當前結帳的人遲遲無法完成,整個排隊的人龍都會收到影響。

由於 HTTP/2 引進了 multiple streams over a single connection (multiplexing) ,因此成功解決了 HTTP 長久以來應用層的 HOL blocking 問題 (來自同一用戶端的 HTTP 請求,必須照接受的順序回應)。但可惜的是,由於 HTTP/2 採用單一 TCP 連線來做資料傳輸,反倒衍生出了傳輸層的 HOL blocking。

傳輸層的 HOL blocking 指的是,在同一個連線當中,即使封包在應用層中是屬於不同的 stream,TCP 依然會要求它們必須依序傳輸。原因在於TCP 是傳輸層協定,並不暸解上方應用層的邏輯,所以對它來說這些封包都屬於同一個連線。因此在網路環境不穩的情境下,不同 Stream 的封包有可能會在傳輸層中因為其他 stream 封包的遺失與重傳而受堵。

HTTP 連線建立過程冗長

HTTP 在建立連線時,必須先完成 TCP 的 3-way handshake,接著如果要採用 secure 連線,還要額外進行 TLS handshake,而這全部加起來需要 3 個 RTT (round trip time)才能完成。這對 HTTP 請求與回應的延遲時間,要求越來越高的現代網路應用而言,是個頭痛的問題。

成也 TCP,敗也 TCP

從上述 HTTP/2 的兩個主要痛點中可以發現,目前主要問題來自 HTTP 所採用傳輸層協定 TCP。TCP 提供了穩定且可靠的封包傳輸機制,也提供了重傳 (re-transmission) 與錯誤處理 (error handling) 等功能,讓基於 TCP 的應用層協定不用擔心資料遺失等問題, HTTP 也是受惠者。但當網路技術越來越發達,基礎網路建設也越來越強健的同時,TCP 繁瑣的溝通機制與缺乏彈性的設計,反倒成為了 HTTP 發展的阻礙。

有鑑於此,為了解決使用 TCP 所造成的問題,在新一代的 HTTP 中,將不再基於 TCP 訂製其傳輸層協定的規格,而是將其描述成兼具效能與可靠性的全新協定。

一個新的傳輸協定的誕生

承上, 由於 TCP 不再適用新一代的 HTTP ,因此勢必得找到其他替代方案,而基本上有兩種選擇 — 既有的傳輸協定或設計新的。在取捨上,除了符合需求外,最重要的議題在於該協定是否可以成功落地

網路協定的僵化 (Protocol Ossification)

網際網路本身是由非常龐大的網路節點所構成,資料的傳輸必須仰賴兩端點間眾多中間節點的協助才能完成。但網際網路的實體其實並沒有大家所想的那麼進步與高科技,相反地,有大量的中間節點非常陳舊與過時。它們可能有數年至數十年未更新使用的軟體,也認不出許多近年才提出的網路協定與延伸功能。由於這些節點老舊且數量龐大,也造成了新的網路協定非常難以推廣與應用

基於 UDP 的新傳輸協定

為了可以順利普及,一個全新的傳輸協定想必是不可行的。因此,大家開始把歪腦筋動到了另外一個普及的傳輸協定 — UDP 身上:

  • 2012 時,Google 開始設計基於 UDP 的實驗性傳輸協定 — Quick UDP Internet Connections
  • 2015 時,Google 正式向 IETF 提出協定草案 (簡稱 gQUIC),內容包含 Google 如何實作與整合 gQUIC 與 HTTP/2 。接著 IETF 於同年成立 QUIC workgroup。
  • 2016 時, QUIC workgroup 正式提出協定草案(簡稱 ITEF-QUIC 或 QUIC),並著手擬訂細節。

ITEF-QUIC 與 gQUIC 的概念一致,但實作並不相同:

  • gQUIC 基於 Google 的需求,採用客製化的加密方式,且只服務 HTTP。
  • ITEF-QUIC 則被設計成可適用不同應用層協定,並採用標準的 TLS 1.3 (RFC-8446)加密。為此,IETF 將整個 QUIC 中關於傳輸層與應用層的規格切成兩個協定草案,即 QUIC 與 HTTP-over-QUIC (後者於 2018 時被正名為 HTTP/3)。
Overview of HTTP/3 and QUIC

QUIC 的特徵

QUIC 主要的特徵包括:

  • 透過 UDP 的封包傳輸 (transfer protocol over UDP)
  • 支援 Connection 與 Stream
  • 可靠的資料傳輸 (reliable data transfers)
  • 流量控制 (flow control and congestion control)
  • 基於 TLS 1.3 的傳輸層加密與 1-RTT 連線建立 ( secure and transport handshakes)
  • 支援 0-RTT 快速交握 (fast handshakes and early data)

我們將依序介紹這些特徵。

透過 UDP 的封包傳輸

為了規避網路協定僵化的問題, QUIC 會把資料封裝成加密過的 UDP 封包來進行傳輸:

  • 對於中間經手的網路節點而言,它們只會看到一個個加密過的 UDP 封包,因此除了導傳外不會多做處理,藉此提升傳輸效能。
  • 對於請求與回應的兩端點而言,則需要在傳輸層負起重傳與錯誤處理等工作,藉此保證資料傳輸的可靠性。

支援 Connection 與 Stream

QUIC 提供了與 HTTP/2 一樣的 multiple streams over a single connection 的概念。差別在於:

  • HTTP/2 是傳輸層的 TCP connection + 應用層的 stream。
  • QUIC 是傳輸層同時提供了 connection 與 stream。

且由於 QUIC 是透過 UDP 來進行資料傳輸,因此它所提供的 connection 與 stream 皆是抽象邏輯,只存在於 client 與 sever 兩節點身上。

相較於 TCP 協定使用 TCP Socket 來建立連線,QUIC 則是採用 UDP Socket + QUIC connection。有別於 TCP socket 需要綁定用戶與伺服器兩端的 IP 與 port,QUIC connection 則是用特有的 connection ID 來辨認連線。

Connection ID

Connection ID 主要有 destination 與 source 兩種類別,都可以用來辨識接收到的 request 屬於哪一個連線。因此對於節點來說,它除了需要維護自身的 connection ID 與連線的對應外,也必須記錄所有連線對象的 connection ID 與連線的對應。

Connection ID Negotiation

本節將會解釋 client 與 server 如何在建立連線的過程中交換彼此的 connection ID,請搭配下圖服用:

Connection ID negotiation in QUIC
  1. Client 初始建立連線
    ・Client 會將自己的 connection ID 填入 request 的 source ID 欄位中。
    ・將 destination ID 填入一個隨機數字。
    ・發送 ClientHello。
  2. Server 收到來自 client 的連線建立請求
    ・Server 將 request 中的 source ID 保存在自己的 destination ID 清單中。
    ・在 response 的 source ID 欄位中填入自己的 connection ID。
    ・在 response 的 destination ID 欄位中填入對方的 connection ID。
    ・發送 ServerHello ,以通知 client 連線建立成功。
  3. Client 收到 ServerHello
    ・Client 將 response 中的 source ID 保存在自己的 destination ID 清單中。
    ・至此,雙方完成 connection ID 的交換。
  4. 進行一般的資料傳輸 (request/response)
    ・在進行一般的資料傳輸時,請求方只會在 request 中填入 destination ID 欄位,並不會攜帶 source ID 欄位。
    ・QUIC 的連線雙方,會預設對方可以辨識出連線,並用對應的 connection ID 來回應。
  5. Connection ID 的新增
    ・一個連線所用的 Connection ID 可以透過傳送 NEW_CONNECTION_ID 這個 frame 來新增。
    ・交換的過程與一開始的 connection ID 交換是相同的。

Connection Migration

由於 QUIC connection 不再綁定 IP 與 port,所以一個連線的生命週期,與clinet 及 server 的實體網路狀態無關。因此 QUIC 可以實現 TCP 所做不到的 connection migration,即允許雙方節點在不重建連線的情況下更換底層的網路環境。

Streams

QUIC stream 除了實作於傳輸層外,大致上的特性與 HTTP/2 的版本一致:

  • 對於同一個 Stream 內的資料,保證其請求與接受順序一致 (in-order delivery within streams)。
  • 對於不同 Stream 內的資料,不保證之間的順序 (out-of-order delivery between streams)
  • 提供雙向 (bidirectional) 與單向 (unidirectional) 兩種 stream 類別。

此外,也提供了 stream prioritizationstream state 等功能,用以控制資料傳輸的順序與流量。

可靠的資料傳輸

由於 QUIC 透過 UDP 來傳輸封包,因此資料的可靠性得仰賴 client 與 sever 兩節點。與 TCP 只提供 connection-level 的資料可靠性相比, QUIC 透過 stream state 的方式提供了 stream-level 的資料可靠性。因此不同stream 的資料重傳與錯誤處理將完全獨立。

流量控制

與資料可靠性相仿, QUIC 的流量控制也必須仰賴 client 與 sever 兩節點負責。QUIC 同時提供了 connection-level 與 stream-level 兩種層級的流量控制。

基於 TLS 1.3 的傳輸層加密與 1-RTT 交握

QUIC 並不提供明文(cleartext)的資料傳輸。且為了提升資料傳輸的安全性, QUIC 只支援 TLS 1.3 以上的傳輸層安全性協定。

TLS 1.2 vs TLS 1.3

除了保障資料傳輸的安全外,採用 TLS 1.3 的最大好處在於它只需要 1-RTT 便可以完成 TLS handshake。而 QUIC 也在進行 TLS handshake 的同時,順道完成整個 QUIC 連線的建立,整體包含 version negotiation、cryptographic 、transport handshake 與 TLS handshake。

相比於 HTTP + TLS 需要 3-RTT 的冗長連線建立過程,只需要 1-RTT 的 QUIC 大幅減少了連線建立的時間。

支援 0-RTT 快速交握

除了 1-RTT 交握模式外,QUIC 也支援了 0-RTT 的快速交握機制。 0-RTT 交握其實依舊需要進行1-RTT 的連線建立,但 client 可以在建立連線的同時,攜帶加密過的請求資料 (early data),server 則在回應連線建立成功的同時,回傳處理結果

Fast handshakes with early data

需要注意的是,0-RTT 交握並不是沒有使用上的限制。必須達成以下條件才能使用:

  • Client 與 server 必須曾經建立過連線。
  • 雙方必須還保有之前連線所使用的 PSKcertificate
  • Early data 必須用之前連線所用的金鑰加密。
  • Server 必須願意支援 0-RTT 快速交握 。由於快速交握必須在還未確認連線建立狀況的階段先行處理資料,除了額外的資源消耗,也有資安上的風險。

HTTP/2 vs HTTP/3

在介紹完 QUIC 的主要特徵後,接著來談談 HTTP/3 在行為與特徵上,與 HTTP/2 有什麼差別。大致上可以統整成下方的比較表:

HTTP/2 vs HTTP/3

HTTP/2 與 HTTP/3 的相同點

HTTP/2 與 HTTP/3 兩者皆支援:

  • 相同的 HTTP syntax 與 HTTP frame
  • Connection 與 stream 的概念
  • Stream multiplexing 與 stream prioritization
  • Stream-level 的 flow control 與 congestion control
  • TLS handshake 與 fast handshake
  • Server push
  • Head compression

HTTP/2 與 HTTP/3 的相異點

  • HTTP/2 的 stream 是實作在應用層,而 HTTP/3 則是實作於傳輸層。因此 HTTP/3 可以避免傳輸層的 HOL blocking 問題,並提供更細緻的 flow control 與 congestion control (應用層 vs 傳輸層)。
  • 以傳輸效率來看,HTTP/2 仰賴 TCP socket 進行資料傳輸, HTTP/3 則是透過 UDP Socket + user space 上的 QUIC 實作來達成。 HTTP/3 可以提供更為有效的資料傳輸
  • 以系統資源消耗的角度而言,由於 TCP 有著十幾年的演算法與 Kernal 的優化,甚至有硬體的支援。在面對同樣量級的請求時,目前基於 QUIC 的 HTTP/3 會比基於 TCP 的HTTP/2 消耗更多的系統資源
  • HTTP/3 不再提供明文的資料傳輸
  • HTTP/3 的連線建立比 HTTP/2 更為快速。且相比於 HTTP/2 所用的 TCP fast open,HTTP/3 所用的 TLS fast handshake 更為容易落地普及。
  • HTTP/2 使用 HPACK 來進行 header compression ,而 HTTP/3 則是採用QPACK
  • HTTP/3 支援 connection migration,對於行動網路環境的 client 更為友善

常見的批評與顧慮

HTTP/3 與 QUIC 常見的批評包括:

  • UDP will never work.
  • UDP is slow in kernels.
  • UDP is too easy to DDOS
  • QUIC takes too much CPU.
  • We are not Google.
  • Too small of an improvement.

可以從上發現大家在意的點著重於資安隱憂資源消耗

關於安全的部分,其實 QUIC 的規格書中有詳細說明要如何避免 UDP 常見的資安議題,也可以在 QUIC workgroup 的 mailing list 中找到非常多相關的討論,安全的議題是 QUIC workgroup 非常重視的一環,也是決定普及成敗的重要因素。因此,筆者相信最終版本的 QUIC 可以設計出完善的方案,以解釋目前對於 UDP 及 QUIC 的安全疑慮。

至於的資源消耗的問題,主要癥結在於長久以來網路的世界都被 TCP 所統治,效能優化也都是基於 TCP/ IP 的需求而發展。因此基於 UDP 的應用技術勢必會相對弱勢 。但只要 HTTP/3 能被逐漸採納,那相關的優化研究自然地會發展前進,因此這是個在未來預期可被解決的問題。透過 HTTP 這塊大旗來推廣 QUIC ,是 QUIC workgroup 所擬定的發展策略中,非常聰明的一手。

總結

在本文章中,我們談論了目前 HTTP/2 的缺點,並解釋了 HTTP/3 如何藉由採用 QUIC 這個新的傳輸層協定解決這些問題。此外,我們也介紹了 QUIC 的特徵與運作原理,並比較了 HTTP/2 與 HTTP/3 兩者間的差異。

總得來說, 基於 QUIC 的 HTTP/3 是個非常令人期待的技術發展。儘管有許多人並不看好 HTTP/3 ,認為它改進的幅度有限,也會像 IPv6 那樣曲高和寡。但 HTTP/3 之所以令人感到期待的並不純粹是因為功能上的改進,而是其背後所藏的巨大野心 — 醞釀一場顛覆主流 TCP/IP 架構的大革命。

可以預期 HTTP/3 與 QUIC 的實踐與普及,並不是近年內會發生的事。但作為如筆者般的開發工程師或網路技術愛好者,我們能做的便是分享與關注,且期盼它對網路技術與網路應用服務帶來更多的激盪與火花。

感謝大家的閱讀。如果有任何問題歡迎在下方留言討論。如果喜歡本文內容,也歡迎大家轉載並給予本文一些掌聲 (clap),謝謝。

參考資料

--

--