TLS 1.3 / QUIC 與 HTTP/3 對效能的改善
QUIC (Quick UDP Internet Connection) 是一個新的網路協定,但是和 IPv6 這樣的改變不同的是,它是一個架構在現有 UDP 之上的 application layer 通訊協定,因此目前在 internet 上的現有 Router / Switch / Firewall … 等等現有的設備並不需因為使用了 QUIC 或是 HTTP/3 而需要做設備的升級。
QUIC 最大的改進(或是稱之為 “改變”),是把 “連線 (Connection)” 這個概念從作業系統 kernel 內部解放出來,讓作業系統的應用程式有機會因爲自己的需求來決定交握 (handshake) 策略,封包遺失(packet lost handling) 策略或是壅塞(congestion control)策略這一類的流量控制(flow control) 的方法,也可擺脫傳統 TCP 3 way 交握 (handshake) 然後在 TCP 連線上面還要跑 TLS 交握和密鑰交換這樣多步的交互流程,對於起始連線反應速度的提升有莫大的幫助。想想看,當你使用的是衛星通訊技術,Gbps 的頻寬已經不是什麼問題,但是衛星通訊 RTT (RoundTrip Time) 的代價相對頻寬來說是非常高的,地面站和同步衛星間(例如 GPS 衛星) 之距離約35000公里,故其間之資料傳輸約需費時 250ms,所以對衛星的一個 RTT 就需要 500ms。這也是為什麼大家的(包含馬斯克)的衛星在搶的是 700km~1,200km 的低高度軌道,這樣 round-trip delay 才能控制在可接受的範圍內。
QUIC 和 HTTP/3 的發展沿革
HTTP/3 是新的第三個主要版本的 HTTP 協定,HTTP/1 與 HTTP/2 都是基於 TCP 協定,長在 TCP Payload 上面的 HTTP 協定。而 HTTP/3 基本上是捨棄了 TCP 這個連接協定,而使用 QUIC 這個底層傳輸協定來取代,然後將原來的 HTTP 協定建築在新的 QUIC 之上。如下圖所示。所以基本上,HTTP/3 是保留並且沿用了 HTTP/2 的一些較 HTTP/1 先進功能,例如 Binary Format / HAPCK (header compression) 這些傳輸層之上的功能,而改進了 HTTP/2 的 Multiplexing 功能使用 QUIC 協定來取代。
QUIC 協定是基於 UDP 的通訊協定,這個改變是為了解決 HTTP/1 和 HTTP/2 都存在的隊頭阻塞 (Head-of-Line Blocking, HOL) 問題,雖然 HTTP/2 在解決 HOL 問題上面基於 SPDY 協定做了很多改進,但是因為其 HTTP/1, HTTP/2 都是基於 TCP,雖然 SPDY 將 request/response 改進了切成了封包框 (Frame) 的設計,但 TCP 本身的流量控制和其他和可靠性相關的特性本身就會帶來 HOL Blocking 問題而導致多個 request / response 協同運作時候的效率不彰,因此 HTTP/3 希望使用基於 UDP 的 QUIC 通訊協定來改善 HOL 與交握程序太過複雜的問題。
QUIC 想要解決與面對的問題
- 通訊協定的導入門檻 — 要導入一個新的通訊協定總是非常困難的,因為在網路上的節點通常都會排斥或是阻擋不熟悉的通訊協定,因此新的傳輸層協定的導入與進展總是非常慢,不誇張,也許需要十年的時間。
- 通訊協定的實作門檻 — 對應於不同種類網路攻擊的日漸增加,需要一個快速的方法來升級客戶端 (Client Side) 的軟體。傳輸層的實作 (例如 TCP),通常是會由作業系統 (OS) 來管理,所以如果要改善 TCP,那麼就必須升級作業系統,升級作業系統是一件大工程,需要評估與測試各種狀況才能夠讓一個作業系統升級,這種系統層面的升級是需要時間的。
- 協定交握 (Handshake) 的延遲— TCP 與 TLS 是如今網路世界的兩個重要的基石,但是傳輸與安全分層的代價就是交握所造成的代價在網速愈來愈快的時代會更加明顯。TCP 起始的 3 向交握至少會有一次的 RTT (round-trip) 延遲,而 TLS 在起始的時候又多加了兩次的 RTT 延遲,當網路愈來愈快的時候,這些起始的 RTT 延遲就會佔用很大部分的時間,對於高速網路的反應時間造成很大的影響。
- 隊頭阻塞 (Head-of-line Blocking) 延遲 —TCP HOL帶來的問題具有普遍性,只要使用了TCP,就有 HOL Blocking 的問題,影響範圍非常廣泛。 而又因為 TCP 的實作取決於作業系統,修改 TCP 用來解決 HOL blocking 或是 security 並非不可能,但是問題在於更動作業系統的代價與時間非常高昂,因此在應用端實作並修改流量控制 (Flow Control) 與安全性的功能變成一個比較可行的方法。這裡有一篇說明 HOL 的文章。
因此 QUIC 的設計,需要滿足下列的需求
- 要能容易地部署
- 協定內建對安全性的設計
- 需要消除交握時期的對頭阻塞延遲
連線的交握 (Handshake)
RTT 是 Round-Trip-Time 的縮寫,在交握的流程裡面,RTT 是影響效率的一個很重大的因素,1 個 RTT 代表一方發出需求之後,必須 “等到” 另外一方回應才能繼續下一個動作,因此在設計通訊協定的時候,對於效率的追求就是 0-RTT,底下這張圖說明 RTT 對性能的影響。這篇文章有詳細的說明。
上面這兩張圖呈現了不同頻寬與遲延時間 (Latency) 對於網頁載入時間 (Page Load Time, PLT) 的影響。可以清楚地看到,頻寬從 1Mbps 升級到 2Mbps 對於 PLT 的改善有顯著的影響。但是隨著頻寬突破 5Mbps 到 10Mbps,對於 PLT 只有 5% 的改善。而在我們 WiFi 或是 5G 頻寬已經論 Gbps 為單位的環境下,頻寬的改善對 PLT 來說,已經是非常有限了。
下面我們討論 TLS 1.2 到 TLS 1.3 在這方面的改進。在進入 QUIC 的設計與實作的說明之前,我們先來看一下 TCP 的交握流程與 TLS 1.2 / TLS 1.3 的交握流程。
TCP 的交握
我們熟悉的 TCP 在建立連線的時候,如下圖,他需要 1.5-RTT 時間的交握流程,縱使我們在 TLS 的設計層次上讓 0-RTT 交握變得可能 (TLS 1.3 連線恢復就是 0-RTT,後面會說明),這個 TCP 交握通常和 OS 綁定在系統內,無法容易更動。
TLS 的交握 ( 從 2-RTT 到 0-RTT )
在 TLS 連線一開始的時候,需要有交握 (Handshake) 的動作讓 Client 和 Server 雙方同意使用哪種加密方式與哪一個密鑰連線。TLS 1.2 需要至少 2 個 RTT 才能完成完整的交握程序。
TLS 1.3 可以比較快完成交握是因為這個至少 2 RTT 的交握程序根本就不會啟動。改進的地方在於 TLS 1.3 的 Client 第一個 HELLO 封包裡面就已經包含了打算使用哪種加密方式,密鑰和其他的必要資訊。Server 直接回應選擇的加密方式和共享密鑰的資訊 (透過迪菲-赫爾曼密鑰交換 — DH 算法,Client 和 Server 可以使用不安全的通道傳遞訊息,並且計算出共享密鑰)。因為 Server 馬上就會回應密鑰資訊,少了一次動作,也讓 TLS 1.3 的連線會更安全。下圖說明 TLS 1.2 → TLS 1.3 的交握機制的不同,從 2-RTT 減低到 1-RTT,增進了 TLS 協定的效率。
0-RTT 的 TLS 1.3 連線恢復交握
TLS 1.3 也改善了連線恢復時候的交握程序,使用不同的做法讓連線恢復的時候允許 0-RTT 的交握,可以提供較快與順暢的網站體驗。使用 TLS1.3 的 0-RTT,在 TLS 連線交握完全執行之前,就可以發送第一個 REQUEST,這個功能,尤其在行動裝置上,瀏覽同一個網站的內容,可以得到很大的改善。
TLS 1.2 使用了 session ID 和 ticket 來回復連線的 session。TLS 1.2 Server 可以自己決定要不要接受回覆連線的要求,或者是重啟一個全新的 TLS 交握過程。這個回復機制是經由稍微修改前一次的交握參數,並且使用現有已經存在的的 TCP session 與 TLS session 來達成。
但是這個方式並不安全,因此 TLS 1.3 使用了 PSK (pre-shared key) 機制導入了一個更加安全的做法。TLS 1.3 在連線回復之前會重新啟動一個新的 TCP 連線,但是在這個 TCP 連線上面使用之前同樣的 TLS 參數和 KEY。PSK 在初始交握的時候就取得,並且應用在後續的回復交握過程中。
QUIC 的交握程序
事實上,QUIC 通訊協定的交握階段就是整合了 TCP 與 TLS 1.3 的交握,每一個交握封包攜帶了更多資訊。在 QUIC 的連線機制下,初始的連線交握直接和金鑰的交換 (藉由 DH 算法) 整合在一起,只需要花一個 RTT 的時間,而從第二次連線之後的每一次連線都可以直接傳送數據,我們稱它 0-RTT 的交握延遲,就可以直接在這個加密通道內傳送數據資料,大幅減低傳統 TCP + TLS 的交握時間。
下圖表示了 QUIC 在初始交握時候的幾種狀況:
QUIC 的彈性
TCP 協議通常會由作業系統來實現,比如 Linux 系統,如果要修改 TCP 的壅塞策略,那就必須要改 kernel,這工作相對龐大,但是使用 QUIC,這些策略被移動到應用層來實現,可以隨時做修改與測試。對於效能至上的應用來說,配合 eBPF 有機會再將性能大幅提升。而 QUIC 在傳輸的過程中所用到的流量控制(Flow Control ) 和提供的可靠性 (Lossless / Reliability) 策略其實和 TCP 差異不大,我認為他對 latency 最大提升的地方,還是把 TCP / TLS 的交握做進一步的整合,能讓 RTT 盡量縮小,而還能保持一致的安全性能。
這邊有針對 QUIC 細節作詳細說明的文章,值得一看。