QUIC 出現背景
大家好,我是 Dan 先生。新的《戰神》即將到來,到處都是 TCP,不過我們可以使用 UDP,並操作 QoS。每次有新技術出現,很多人就像剛才的鴨子一樣,擁抱新技術又抱怨新技術。但無論如何,最基本的是要了解新技術。今天我們要談的是 QUIC。
HTTP2 基本通信模型
在了解 QUIC 之前,我們需要快速回顧一下 HTTP2 的基本通信模型。靠近用戶的應用層使用 HTTP2 協議,這個協議除了基本的 HTTP 語言,還有特殊的多路復用和流量控制。雖然 HTTP2 不要求加密,但基本上都會實現加密,所以下一步就是 TLS。TLS 層負責加密前的握手以及身份驗證、完整性和保密性,簡單說就是加密那些數據。上層應用下來的數據到達傳輸層,即使用 TCP 進行可靠傳輸。TCP 會負責通信前的握手,以及流量控制。傳輸層下來是網絡層,也就是 IP 層。
QUIC 網絡模型
QUIC 的網絡模型與上述不同。首先,QUIC 帶來了 HTTPS 的多層復用和流量控制的好處,還整合了 TCB 的擁塞控制、流量控制和傳輸層機制,最後把 TLS 加密相關的東西都整合在一起。雖然 TLS 仍然存在,但它只是 QUIC 的一部分。由於 QUIC 進行了大量整合和改進,它可以直接在 HTTP2 下工作,這也導致了一些不適,因此 QUIC 直接推出了新的 HTTP 協議,即 HTTP3。
HTTP3 基本上保留了 HTTP 的基本語言,HTTP2 的許多功能都交給了 QUIC。但操作系統暫時沒有 QUIC 協議,所以 QUIC 還得依靠舊的傳輸協議。不能選擇 TCP,因為 QUIC 本應是 TCP 2.0,所以只能選擇 UDP。使用 UDP 不能像 TCP 那樣按順序傳輸,而且大力推廣 HTTP3 卻要選擇被運營商嫌棄的 UDP,可謂有面子也有難處。IP 層則保持不變。
QUIC 協議層次定位
QUIC 很奇怪,雖然它被劃分為傳輸層協議,但仍然依賴舊式的傳輸層協議 UDP,否則無法完成應用進程之間的傳輸。如果把 QUIC 劃分為應用層協議,雖然定義上不太準確,但也有道理,因為 QUIC 實際上是在應用中實現的,這也是為了讓 QUIC 能夠快速廣泛部署,而不用等到操作系統完全升級。QUIC 就像編程語言 Python,本身可能沒有什麼實質性的東西,而是來自其他地方的各種組合。
QUIC 數據包結構
QUIC 的數據包結構很複雜,形成了一個繞口令:一個 Ethereum 針把 IP 包封在裡面,又把 UDP 段封在裡面,再把一個或多個 QUIC 包封在裡面,又把一個或多個 QUIC 針封在裡面,最後把應用數據封在裡面。我們可以這樣理解:假設有一頭豬,因為它太大,無法一次性從這裡運到那裡,所以要把它切成無數塊豬肉。把這些豬肉排好,就形成了豬血。把豬肉分成這麼小的單位後,就可以把豬血組合成豬肉蒸。為了更好地運輸豬肉排,把豬肉排封在豬肉袋裡。現在每個豬肉袋都有自己的編號,每個豬肉袋裡有多少塊豬肉,每塊豬肉都有記錄。原來的豬就相當於我們的應用數據,應用數據被分成流,流再組合成針,最後針被封在 QUIC 包裡。
QUIC 的分佈是一個特點,每個 QUIC 包都有自己獨立的編號。如果丟失一個 QUIC 包,就可以相應地知道這個包裡丟失了什麼數據流,其他 QUIC 包不需要停止等待恢復,只需要轉發必要的 QUIC 包就可以解決 TCP 的隊頭阻塞問題。但如果丟失的數據只是主機瀏覽器渲染頁面的一部分,比如 CSS 文件的一部分,我們的頁面仍然無法渲染。解決了隊頭阻塞問題,又出現了另一個問題,就像打字一樣,這個頭剛解決,另一個頭又出來了。
QUIC 的 0 RTT 機制
除了理論上解決 TCP 的問題,QUIC 還有一個被吹上天的機制,就是 0 RTT。對於 HTTP2,通信前會有 TCP 三次握手,一般認為這裡會產生 1 RTT(1 個往返時間),然後進行加密。如果使用 TLS 1.2,會導致 2 RTT,後面才是正常的加密通信,所以使用 TLS1.2 的 HTTPS2 至少會產生 3 RTT。如果使用 TLS1.3 的 HTTPS2,加密握手會減少一輪,整體變成 2 RTT。對於使用 QUIC 的 HTTPS3,由於 TCP 和 TLS 握手整合在一起,所以變成 1 RTT,之後就是加密通信,因為通信雙方所需的各種參數已經可用。如果需要恢復通信,可以直接使用加密參數進行通信,使整個通信連接變成 0 RTT。可以理解為 ERTT 是 QUIC 的第一次通信,0 RTT 是通信的恢復。
QUIC 握手過程
但 QUIC 的握手過程並不像介紹的那麼簡單。實際上,QUIC 的 ERTT 握手本質是 TCP 和 TLS。一開始,客戶端會發送一個 client hello 打招呼,然後服務端會回應 server hello 確認。這兩步可以理解為加密初始化,即 initial crypto。這兩步完成後,後面的信息就是加密信息。
具體來看,客戶端發送的 QUIC 包屬於初始握手信息,包的真實類型是 Crypto,被稱為 TLS 1.3 Client Hello。然後服務端回應初始握手信息,包的真實類型也是 Crypto,被稱為 TLS 1.3 Server Hello。下一個包才是真正的握手,但這裡已經有提示說無法解碼,因為這一步已經加密,而不是等到整個握手過程完成才加密。實際上,這一步可以說握手已經完成了大部分。客戶端和服務端需要交換信息才能生成秘密鑰匙。
回到 TLS 1.3 Client Hello,在記錄的信息中,可以看到需要的答案。這裡有一個隨機數,以及加密前需要提供的信息,如加密、壓縮方法等。服務端相應的 server hello 也提供了相應的參數。正是因為這兩步,雙方提供了生成秘密鑰匙所需的加密參數,而中間人不知道這些信息。中間人知道什麼信息呢?可以看看第一個握手包,也就是第一個加密包。除了 IP 地址和端口,QUIC 包裡只有這麼一點信息,最重要的是這裡的連接 ID,後面會講到。
另一個重要信息是,這裡的包頭寫著 Long Header,前幾個 QUIC 包也寫著 Long Header。實際上,QUIC 包分為長頭和短頭,長頭包用於 ERTT 初始連接,短頭包可以在使用 ERTT 秘密鑰匙後使用。畢竟,看看原來的包有多大,如果每個包都這麼大,就沒有意義了,所以後續的包也需要設計得更精簡。
了解這些後,對包進行解碼展示。現在可以看到,在握手步驟中,加密擴展和證書是已知的。這意味著服務端發送 server hello 後,還會一起發送一些安全相關的信息,如證書。這些 QUIC 包一般會分成多個同時發送。確認服務端發送後,客戶端會回應 finish,表示握手正式完成。一般來說,服務端也會回應 handshakedown,表示握手完成。下一步就是正式的數據傳輸,包括 HTTP3 的數據傳輸。
HTTP3 相關
如果看 HTTP3 的信息,會看到流類型顯示了一種叫做 QPAC 的東西。熟悉 HTTP2 的人會記得 HPEC 的壓縮格式,但 HPEC 是用於 TCP 的。為了整合 QUIC,基於 HPAC 開發了一種叫做 QPAC 的壓縮格式。整個過程中最重要的是前面的 ERTT,因為所有信息都整合在這裡。握手已經是加密通信步驟,這與其他協議不同。
如果是 0 RTT,當客戶端和服務端之間傳輸時,需要附加令牌,加密後的應用數據也一起發送,這樣就可以直接回應。當然,其中有很多細節,具體還是關於 TLS 1.3 的技術細節。如果想了解更多關於 TLS 1.3 的信息,記得關注相關公告。
QUIC 對不同對象的影響
實際上,這只是形式上的一個大變化。以前的 HTTP 數據加密後,會添加 TLS 和 TCP,現在 QUIC 完全不同,除了 HTTP 數據,還加密了很多握手信息,比如包的編號、ECK、ZEN 等。對用戶來說,這似乎是個好消息,因為數據得到了更好的保護。但對 ISP 來說,不一定是好事,因為如果想阻止某些流量,很難檢測。畢竟,ISP 能看到的 QUIC 包中最重要的是連接 ID。
連接 ID 的作用
要知道,在進行 TCP 可靠傳輸時,需要基於四個元素建立邏輯連接,即源和目標的 IP 地址以及源和目標的端口。這樣就可以準確定位某個主機的某個服務進程。但如果主機中的某個元素發生變化,就需要重新建立連接。比如,你的手機連接到家裡的 Wi-Fi,但當你離開家走遠時,手機會自動切換到 4G,此時你原來的 IP 地址就會改變,也就是說,需要重新連接 TCP。
為了避免這種斷開和重新連接的重複情況,QUIC 在包中添加了連接 ID。客戶端和服務端會協商連接 ID,大家都用連接 ID 來識別這個邏輯鏈。回到前面的包信息,可以看到客戶端和服務端都有自己的連接 ID,每個包都會添加相應的連接 ID 來提供識別。
不過,這個連接 ID 的實際效果還有待驗證,因為即使有連接 ID,在短時間內切換網絡時,仍然能感受到網絡的延遲。而且更多的連接 ID 相當於需要存儲更多的信息,比如,除了負載均衡的基本四個元素,還需要保存連接 ID。沒辦法改變一些東西,只能在線上添加一些東西。
QUIC 的其他特性
幸好 QUIC 還繼承了 TCP 和 HTTP2 的一些好傳統,比如限制流量。實際上,在開始握手時,就會告訴對方。打開前面的包信息,在 Client Hello 的擴展信息中有 Quick Transport Parameters,如果想深入了解,需要從這裡開始,這是 QUIC 的一些特殊參數,會對數據流進行一些限制。如果數據包需要重新發送,也需要根據一定的信息進行判斷。
QUIC 有一個重要的定義叫做 ACK。如果學過 TCP,就知道 ACK 實際上是 Acknowledgement 的縮寫。只不過這裡的確認機制與 TCP 不同。簡單說,QUIC 就是依靠這個 ACK 定義的內容告訴對方收到了什麼,需要重新發送什麼。
總結
說到這裡,大家應該已經了解了 QUIC 的核心機制。QUIC 在理論上確實解決了很多問題,尤其是在高延遲和容易丟包的網絡環境中,QUIC 會發揮作用,但實際部署中肯定會有很多問題。但對於每一種新技術,沒有一定的資金和政策支持,很難快速廣泛地應用,仍然存在很多困難。這就是本期視頻的內容,下次見。