你是否也有這樣的困擾:打開(kāi) APP 巨耗時(shí)、刷劇一直在緩沖、追熱搜打不開(kāi)頁(yè)面、信號(hào)稍微差點(diǎn)就直接加載失敗……
如果有一個(gè)協(xié)議能讓你的上網(wǎng)速度,在不需要任何修改的情況下就能提升 20%,特別是網(wǎng)絡(luò)差的環(huán)境下能夠提升 30% 以上;如果有一個(gè)協(xié)議可以讓你在 WiFi 和蜂窩數(shù)據(jù)切換時(shí),網(wǎng)絡(luò)完全不斷開(kāi)、直播不卡頓、視頻不緩沖;你愿意去了解一下它嗎?它就是 QUIC 協(xié)議。本文將從 QUIC 的背景、原理、實(shí)踐部署等方面來(lái)詳細(xì)介紹。
網(wǎng)絡(luò)協(xié)議棧 1.1 什么叫網(wǎng)絡(luò)協(xié)議?
類(lèi)似于我們生活中簽署的合同一樣,比如買(mǎi)賣(mài)合同是為了約束買(mǎi)賣(mài)雙方的行為按照合同的要求履行,網(wǎng)絡(luò)協(xié)議是為了約束網(wǎng)絡(luò)通信過(guò)程中各方(客戶(hù)端、服務(wù)端及中間設(shè)備)必須按照協(xié)議的規(guī)定進(jìn)行通信,它制定了數(shù)據(jù)包的格式、數(shù)據(jù)交互的過(guò)程等等,網(wǎng)絡(luò)中的所有設(shè)備都必須嚴(yán)格遵守才可以全網(wǎng)互聯(lián)。
在網(wǎng)絡(luò)協(xié)議棧中,是有分層的,每一層負(fù)責(zé)不同的事務(wù)。我們討論最多的有三個(gè):應(yīng)用層、傳輸層、網(wǎng)絡(luò)層。應(yīng)用層主要是針對(duì)應(yīng)用軟件進(jìn)行約束,比如你訪(fǎng)問(wèn)網(wǎng)站需要按照 HTTP 協(xié)議格式和要求進(jìn)行,你發(fā)送電子郵件需要遵守 SMTP 等郵件協(xié)議的格式和要求;傳輸層主要負(fù)責(zé)數(shù)據(jù)包在網(wǎng)絡(luò)中的傳輸問(wèn)題,比如如何保證數(shù)據(jù)傳輸?shù)臅r(shí)候的安全性和可靠性、數(shù)據(jù)包丟了怎么處理;網(wǎng)絡(luò)層,也叫路由轉(zhuǎn)發(fā)層,主要負(fù)責(zé)數(shù)據(jù)包從出發(fā)地到目的地,應(yīng)該怎樣選擇路徑才能更快的到達(dá)。合理的網(wǎng)絡(luò)協(xié)議能夠讓用戶(hù)上網(wǎng)更快!
1.2 HTTP/3 協(xié)議
HTTP/3 是第三個(gè)主要版本的 HTTP 協(xié)議。與其前任 HTTP/1.1 和 HTTP/2 不同,在 HTTP/3 中,棄用 TCP 協(xié)議,改為使用基于 UDP 協(xié)議的 QUIC 協(xié)議實(shí)現(xiàn)。所以,HTTP/3 的核心在于 QUIC 協(xié)議。顯然,HTTP/3 屬于應(yīng)用層協(xié)議,而它使用的 QUIC 協(xié)議屬于傳輸層協(xié)議。
1.3 我們需要 HTTP/3 協(xié)議嗎
很多人可能都會(huì)有這樣一個(gè)疑問(wèn),為什么在 2015 年才標(biāo)準(zhǔn)化了 HTTP/2 ,這么快就需要 HTTP/3?
我們知道,HTTP/2 通過(guò)引入“流”的概念,實(shí)現(xiàn)了多路復(fù)用。簡(jiǎn)單來(lái)說(shuō),假設(shè)你訪(fǎng)問(wèn)某個(gè)網(wǎng)站需要請(qǐng)求 10 個(gè)資源,你使用 HTTP1.1 協(xié)議只能串行地發(fā)請(qǐng)求,資源 1 請(qǐng)求成功之后才能發(fā)送資源 2 的請(qǐng)求,以此類(lèi)推,這個(gè)過(guò)程是非常耗時(shí)的。如果想 10 個(gè)請(qǐng)求并發(fā),不需要串行等待的話(huà),在 HTTP1.1 中,應(yīng)用就需要為一個(gè)域名同時(shí)建立 10 個(gè) TCP 連接才行(一般瀏覽器不允許建立這么多),這無(wú)疑是對(duì)資源的極大的浪費(fèi)。HTTP/2 的多路復(fù)用解決了這一問(wèn)題,能使多條請(qǐng)求并發(fā)。
但現(xiàn)實(shí)很殘酷,為什么很多業(yè)務(wù)用了 HTTP/2,反倒不如 HTTP1.1 呢?
第一:多流并發(fā)帶來(lái)了請(qǐng)求優(yōu)先級(jí)的問(wèn)題,因?yàn)橛械恼?qǐng)求客戶(hù)端(比如瀏覽器)希望它能盡快返回,有的請(qǐng)求可以晚點(diǎn)返回;又或者有的請(qǐng)求需要依賴(lài)別的請(qǐng)求的資源來(lái)展示。流的優(yōu)先級(jí)表示了這個(gè)請(qǐng)求被處理的優(yōu)先級(jí),比如客戶(hù)端請(qǐng)求的關(guān)鍵的 CSS 和 JS 資源是必須高優(yōu)先級(jí)返回的,圖片視頻等資源可以晚一點(diǎn)響應(yīng)。流的優(yōu)先級(jí)的設(shè)置是一個(gè)難以平衡或者難以做到公平合理的事情,如果設(shè)置稍微不恰當(dāng),就會(huì)導(dǎo)致有些請(qǐng)求很慢,這在用戶(hù)看來(lái),就是用了 HTTP/2 之后,怎么有的請(qǐng)求變慢了。
第二:HTTP/2 解決了 HTTP 協(xié)議層面的隊(duì)頭阻塞,但是 TCP 的隊(duì)頭阻塞仍然沒(méi)有解決,所有的流都在一條 TCP 連接上,如果萬(wàn)一序號(hào)小的某個(gè)包丟了,那么 TCP 為了保證到達(dá)的有序性,必須等這個(gè)包到達(dá)后才能滑動(dòng)窗口,即使后面的序號(hào)大的包已經(jīng)到達(dá)了也不能被應(yīng)用程序讀取。這就導(dǎo)致了在多條流并發(fā)的時(shí)候,某條流的某個(gè)包丟了,序號(hào)在該包后面的其他流的數(shù)據(jù)都不能被應(yīng)用程序讀取。這種情況下如果換做 HTTP1.1,由于 HTTP1.1 是多條連接,某個(gè)連接上的請(qǐng)求丟包了,并不影響其他連接。所以在丟包比較嚴(yán)重的情況下,HTTP/2 整體效果大概率不如 HTTP1.1
事實(shí)上,我們并不是真的需要新的 HTTP 版本,而是需要對(duì)底層傳輸控制協(xié)議 (TCP) 進(jìn)行升級(jí)。
1.4 QUIC 協(xié)議棧
圖 0-QUIC 協(xié)議棧
QUIC 協(xié)議實(shí)現(xiàn)在用戶(hù)態(tài),建立在內(nèi)核態(tài)的 UDP 的基礎(chǔ)之上,集成了 TCP 的可靠傳輸特性,集成了 TLS1.3 協(xié)議,保證了用戶(hù)數(shù)據(jù)傳輸?shù)陌踩?/p>
QUIC 協(xié)議的優(yōu)秀特性 2.1 建連快
數(shù)據(jù)的發(fā)送和接收,要想保證安全和可靠,一定是需要連接的。TCP 需要,QUIC 也同樣需要。連接到底是什么?連接是一個(gè)通道,是在一個(gè)客戶(hù)端和一個(gè)服務(wù)端之間的唯一一條可信的通道,主要是為了安全考慮,建立了連接,也就是建立了可信通道,服務(wù)器對(duì)這個(gè)客戶(hù)端“很放心”,對(duì)于服務(wù)器來(lái)說(shuō):你想跟我進(jìn)行通信,得先讓我認(rèn)識(shí)一下你,我得先確認(rèn)一下你是好人,是有資格跟我通信的。那么這個(gè)確認(rèn)對(duì)方身份的過(guò)程,就是建立連接的過(guò)程。
傳統(tǒng)基于 TCP 的 HTTPS 的建連過(guò)程為什么如此慢?它需要 TCP 和 TLS 兩個(gè)建連過(guò)程。如圖 1 所示(傳統(tǒng) HTTPS 請(qǐng)求流程圖):
圖 1- 傳統(tǒng) HTTPS 請(qǐng)求流程圖
對(duì)于一個(gè)小請(qǐng)求(用戶(hù)數(shù)據(jù)量較?。┒?,傳輸數(shù)據(jù)只需要 1 個(gè) RTT,但是光建連就花掉了 3 個(gè) RTT,這是非常不劃算的,這里建連包括兩個(gè)過(guò)程:TCP 建連需要 1 個(gè) RTT,TLS 建連需要 2 個(gè) RTT。RTT:Round Trip Time,數(shù)據(jù)包在網(wǎng)絡(luò)上一個(gè)來(lái)回的時(shí)間。
為什么需要兩個(gè)過(guò)程?可惡就可惡在這個(gè)地方,TCP 和 TLS 沒(méi)辦法合并,因?yàn)?TCP 是在內(nèi)核里完成的,TLS 是在用戶(hù)態(tài)。也許有人會(huì)說(shuō)把干掉內(nèi)核里的 TCP,把 TCP 挪出來(lái)放到用戶(hù)態(tài),然后就可以和 TLS 一起處理了。首先,你干不掉內(nèi)核里的 TCP,TCP 太古老了,全世界的服務(wù)器的 TCP 都固化在內(nèi)核里了。所以,既然干不掉 TCP,那我不用它了,我再自創(chuàng)一個(gè)傳輸層協(xié)議,放到用戶(hù)態(tài),然后再結(jié)合 TLS,這樣不就可以把兩個(gè)建連過(guò)程合二為一了嗎?是的,這就是 QUIC。
2.1.1 QUIC 的 1-RTT 建連
如圖 2 所示,是 QUIC 的連接建立過(guò)程:初次建連只需要 1 個(gè) RTT 即可完成建連。后續(xù)再次建連就可以使用 0-RTT 特性
圖 2-QUIC 建連過(guò)程圖
QUIC 的 1-RTT 建連:客戶(hù)端與服務(wù)端初次建連(之前從未進(jìn)行通信過(guò)),或者長(zhǎng)時(shí)間沒(méi)有通信過(guò)(0-RTT 過(guò)期了),只能進(jìn)行 1-RTT 建連。只有先進(jìn)行一次完整的 1-RTT 建連,后續(xù)一段時(shí)間內(nèi)的通信才可以進(jìn)行 0-RTT 建連。
如圖 3 所示:QUIC 的 1-RTT 建連可以分成兩個(gè)部分。QUIC 連接信息部分和 TLS1.3 握手部分。
圖 3-QUIC 建連抓包
QUIC 連接:協(xié)商 QUIC 版本號(hào)、協(xié)商 quic 傳輸參數(shù)、生成連接 ID、確定 Packet Number 等信息,類(lèi)似于 TCP 的 SYN 報(bào)文;保證通信的兩端確認(rèn)過(guò)彼此,是對(duì)的人。
TLS1.3 握手:標(biāo)準(zhǔn)協(xié)議,非對(duì)稱(chēng)加密,目的是為了協(xié)商出 對(duì)稱(chēng)密鑰,然后后續(xù)傳輸?shù)臄?shù)據(jù)使用這個(gè)對(duì)稱(chēng)密鑰進(jìn)行加密和解密,保護(hù)數(shù)據(jù)不被竊取。
我們重點(diǎn)看 QUIC 的 TLS1.3 握手過(guò)程。
圖 4-QUIC 的 1-RTT 握手流程
我們通過(guò)圖 4 可以看到,整個(gè)握手過(guò)程需要 2 次握手(第三次握手是帶了數(shù)據(jù)的),所以整個(gè)握手過(guò)程只需要 1-RTT(RTT 是指數(shù)據(jù)包在網(wǎng)絡(luò)上的一個(gè)來(lái)回)的時(shí)間。
1-RTT 的握手主要包含兩個(gè)過(guò)程:
客戶(hù)端發(fā)送 Client Hello 給服務(wù)端;
服務(wù)端回復(fù) Server Hello 給客戶(hù)端;
我們通過(guò)下圖中圖 5 和圖 6 來(lái)看 Client Hello 和 Server Hello 具體都做了啥:
第一次握手(Client Hello 報(bào)文)
圖 5-Client Hello 報(bào)文
首先,Client Hello 在擴(kuò)展字段里標(biāo)明了支持的 TLS 版本(Supported Version:TLS1.3)。值得注意的是 Version 字段必須要是 TLS1.2,這是因?yàn)?TLS1.2 已經(jīng)在互聯(lián)網(wǎng)上存在了 10 年。網(wǎng)絡(luò)中大量的網(wǎng)絡(luò)中間設(shè)備都十分老舊,這些網(wǎng)絡(luò)設(shè)備會(huì)識(shí)別中間的 TLS 握手頭部,所以 TLS1.3 的出現(xiàn)如果引入了未知的 TLS Version 必然會(huì)存在大量的握手失敗。
圖 6-Client Hello 報(bào)文
其次,ClientHello 中包含了非常重要的 key_share 擴(kuò)展:客戶(hù)端在發(fā)送之前,會(huì)自己根據(jù) DHE 算法生成一個(gè)公私鑰對(duì)。發(fā)送 Client Hello 報(bào)文的時(shí)候會(huì)把這個(gè)公鑰發(fā)過(guò)去,那么這個(gè)公鑰就存在于 key_share 中,key_share 還包含了客戶(hù)端所選擇的曲線(xiàn) X25519??傊琸ey_share 是客戶(hù)端提前生成好的公鑰信息。
最后,Client Hello 里還包括了:客戶(hù)端支持的算法套、客戶(hù)端所支持的橢圓曲線(xiàn)以及簽名算法、psk 的模式等等,一起發(fā)給服務(wù)端。
圖 7-Client Hello 報(bào)文
第二次握手:(Server Hello 報(bào)文)
圖 8-Server Hello 報(bào)文
服務(wù)端自己根據(jù) DHE 算法也生成了一個(gè)公私鑰對(duì),同樣的,Key_share 擴(kuò)展信息中也包含了 服務(wù)端的公鑰信息。服務(wù)端通過(guò) ServerHello 報(bào)文將這些信息發(fā)送給客戶(hù)端。
至此為止,雙方(客戶(hù)端服務(wù)端)都拿到了對(duì)方的公鑰信息,然后結(jié)合自己的私鑰信息,生成 pre-master key,在這里官方的叫法是(client_handshake_traffic_secret 和server_handshake_traffic_secret),然后根據(jù)以下算法進(jìn)行算出 key 和 iv,使用 key 和 iv 對(duì) Server Hello 之后所有的握手消息進(jìn)行加密。
注意:在握手完成之后,服務(wù)端會(huì)發(fā)送一個(gè) New Session Ticket 報(bào)文給客戶(hù)端,這個(gè)包非常重要,這是 0-RTT 實(shí)現(xiàn)的基礎(chǔ)。
圖 9-New Session Ticket 報(bào)文
2.1.2 QUIC 的 0-RTT 握手
這個(gè)功能類(lèi)似于 TLS1.2 的會(huì)話(huà)復(fù)用,或者說(shuō) 0-RTT 是基于會(huì)話(huà)復(fù)用功能的。
圖 10- QUIC 的 0-RTT 流程圖
通過(guò)上面圖 10 我們可以看到,client 和 server 在建連時(shí),仍然需要兩次握手,仍然需要 1 個(gè) rtt,但是為什么我們說(shuō)這是 0-rtt 呢,是因?yàn)?client 在發(fā)送第一個(gè)包 client hello 時(shí),就帶上了數(shù)據(jù)(HTTP 請(qǐng)求),從什么時(shí)候開(kāi)始發(fā)送數(shù)據(jù)這個(gè)角度上來(lái)看,的確是 0-RTT。
我們通過(guò)抓包來(lái)看 0-RTT 的過(guò)程:
圖 11- QUIC 的 0-RTT 抓包
所以真正在實(shí)現(xiàn) 0-RTT 的時(shí)候,請(qǐng)求的數(shù)據(jù)并不會(huì)跟 Initial 報(bào)文(內(nèi)含 Client Hello)一起發(fā)送,而是單獨(dú)一個(gè)數(shù)據(jù)包(0-RTT 包)進(jìn)行發(fā)送,只不過(guò)是跟 Initial 包同時(shí)進(jìn)行發(fā)送而已。
圖 12- QUIC 的 0-RTT 包
我們單獨(dú)看 Initial 報(bào)文發(fā)現(xiàn),除了 pre_share_key、early-data 標(biāo)識(shí)等信息與 1-RTT 時(shí)不同,其他并無(wú)區(qū)別。
2.1.3 QUIC 建連需要注意的問(wèn)題
第一,QUIC 實(shí)現(xiàn)的時(shí)候,必須緩存收到的亂序加密幀,這個(gè)緩存至少要大于 4096 字節(jié)。當(dāng)然可以選擇緩存更多的數(shù)據(jù),更大的緩存上限意味著可以交換更大的密鑰或證書(shū)。終端的緩存區(qū)大小不必在整個(gè)連接生命周期內(nèi)保持不變。這里記?。簛y序幀一定要緩存下來(lái)。如果不緩存,會(huì)導(dǎo)致連接失敗。如果終端的緩存區(qū)不夠用了,則其可以通過(guò)暫時(shí)擴(kuò)大緩存空間確保握手完成。如果終端不擴(kuò)大其緩存,則其必須以錯(cuò)誤碼 CRYPTO_BUFFER_EXCEEDED 關(guān)閉連接。
第二,0-RTT 存在前向安全問(wèn)題,請(qǐng)慎用!
2.2 連接遷移
QUIC 通過(guò)連接 ID 實(shí)現(xiàn)了連接遷移。
我們經(jīng)常需要在 WiFi 和 4G 之間進(jìn)行切換,比如我們?cè)诩依飼r(shí)使用 WiFi,出門(mén)在路上,切換到 4G 或 5G,到了商場(chǎng),又連上了商場(chǎng)的 WiFi,到了餐廳,又切換到了餐廳的 WiFi,所以我們的日常生活中需要經(jīng)常性的切換網(wǎng)絡(luò),那每一次的切換網(wǎng)絡(luò),都將導(dǎo)致我們的 IP 地址發(fā)生變化。
傳統(tǒng)的 TCP 協(xié)議是以四元組(源 IP 地址、源端口號(hào)、目的 ID 地址、目的端口號(hào))來(lái)標(biāo)識(shí)一條連接,那么一旦四元組的任何一個(gè)元素發(fā)生了改變,這條連接就會(huì)斷掉,那么這條連接中正在傳輸?shù)臄?shù)據(jù)就會(huì)斷掉,切換到新的網(wǎng)絡(luò)后可能需要重新去建立連接,然后重新發(fā)送數(shù)據(jù)。這將會(huì)導(dǎo)致用戶(hù)的網(wǎng)絡(luò)會(huì)“卡”一下。
但是,QUIC 不再以四元組作為唯一標(biāo)識(shí),QUIC 使用連接 ID 來(lái)標(biāo)識(shí)一條連接,無(wú)論你的網(wǎng)絡(luò)如何切換,只要連接 ID 不變,那么這條連接就不會(huì)斷,這就叫連接遷移!
圖 13-QUIC 連接遷移介紹
2.2.1 連接 ID
每條連接擁有一組連接標(biāo)識(shí)符,也就是連接 ID,每個(gè)連接 ID 都能標(biāo)識(shí)這條連接。連接 ID 是由一端獨(dú)立選擇的,每個(gè)端(客戶(hù)端和服務(wù)端統(tǒng)稱(chēng)為端)選擇連接 ID 供對(duì)端使用。也就是說(shuō),客戶(hù)端生成的連接 ID 供服務(wù)端使用(服務(wù)端發(fā)送數(shù)據(jù)時(shí)使用客戶(hù)端生成的連接 ID 作為目的連接 ID),反過(guò)來(lái)一樣的。
連接 ID 的主要功能是確保底層協(xié)議(UDP、IP 及更底層的協(xié)議棧)發(fā)生地址變更(比如 IP 地址變了,或者端口號(hào)變了)時(shí)不會(huì)導(dǎo)致一個(gè) QUIC 連接的數(shù)據(jù)包被傳輸?shù)藉e(cuò)誤的 QUIC 終端(客戶(hù)端和服務(wù)端統(tǒng)稱(chēng)為終端)上。
2.2.2 QUIC 的連接遷移過(guò)程
QUIC 限制連接遷移為僅客戶(hù)端可以發(fā)起,客戶(hù)端負(fù)責(zé)發(fā)起所有遷移。如果客戶(hù)端接收到了一個(gè)未知的服務(wù)器發(fā)來(lái)的數(shù)據(jù)包,那么客戶(hù)端必須丟棄這些數(shù)據(jù)包。
如圖 14 所示,連接遷移過(guò)程總共需要四個(gè)步驟。
連接遷移之前,客戶(hù)端使用 IP1 和服務(wù)端進(jìn)行通信;
客戶(hù)端 IP 變成 IP2,并且使用 IP2 發(fā)送非探測(cè)幀給服務(wù)端;
啟動(dòng)路徑驗(yàn)證(雙方都需要互相驗(yàn)證),通過(guò) PATH_CHANLLENGE 幀和 PATH_RESPONSE 幀進(jìn)行驗(yàn)證。
驗(yàn)證通過(guò)后,使用 IP2 進(jìn)行通信。
圖 14- 連接遷移流程圖
2.3 解決 TCP 隊(duì)頭阻塞問(wèn)題
在 HTTP/2 中引入了流的概念。目的是實(shí)現(xiàn) 多個(gè)請(qǐng)求在同一個(gè)連接上并發(fā),從而提升網(wǎng)頁(yè)加載的效率。
圖 15-QUIC 解決 TCP 隊(duì)頭阻塞問(wèn)題
由圖 15 來(lái)看,假設(shè)有兩個(gè)請(qǐng)求同時(shí)發(fā)送,紅色的是請(qǐng)求 1,藍(lán)色的是請(qǐng)求 2,這兩個(gè)請(qǐng)求在兩條不同的流中進(jìn)行傳輸。假設(shè)在傳輸過(guò)程中,請(qǐng)求 1 的某個(gè)數(shù)據(jù)包丟了,如果是 TCP,即使請(qǐng)求 2 的所有數(shù)據(jù)包都收到了,但是也只能阻塞在內(nèi)核緩沖區(qū)中,無(wú)法交給應(yīng)用層。但是 QUIC 就不一樣了,請(qǐng)求 1 的數(shù)據(jù)包丟了只會(huì)阻塞請(qǐng)求 1,請(qǐng)求 2 不會(huì)受到阻塞。
有些人不禁發(fā)問(wèn),不是說(shuō) HTTP2 也有流的概念嗎,為什么只有 QUIC 才能解決呢,這個(gè)根本原因就在于,HTTP2 的傳輸層用的 TCP,TCP 的實(shí)現(xiàn)是在內(nèi)核態(tài)的,而流是實(shí)現(xiàn)在用戶(hù)態(tài)度,TCP 是看不到“流”的,所以在 TCP 中,它不知道這個(gè)數(shù)據(jù)包是請(qǐng)求 1 還是請(qǐng)求 2 的,只會(huì)根據(jù) seq number 來(lái)判斷包的先后順序。
2.4 更優(yōu)的擁塞控制算法
擁塞控制算法中最重要的一個(gè)參數(shù)是 RTT,RTT 的準(zhǔn)確性決定了擁塞控制算法的準(zhǔn)確性;然而,TCP 的 RTT 測(cè)量往往不準(zhǔn)確,QUIC 的 RTT 測(cè)量是準(zhǔn)確的。
圖 16-TCP 計(jì)算 RTT
如圖 16 所示:由于網(wǎng)絡(luò)中經(jīng)常出現(xiàn)丟包,需要重傳,在 TCP 協(xié)議中,初始包和重傳包的序號(hào)是一樣的,擁塞控制算法進(jìn)行計(jì)算 RTT 的時(shí)候,無(wú)法區(qū)別是初始包還是重傳包,這將導(dǎo)致 RTT 的計(jì)算值要么偏大,要么偏小。
圖 17-QUIC 計(jì)算 RTT
如圖 17 所示:QUIC 通過(guò) Packet Number 來(lái)標(biāo)識(shí)包的序號(hào),而且規(guī)定 Packet Number 只能單調(diào)遞增,這也就解決了初始包和重傳包的二義性。從而保證 RTT 的值是準(zhǔn)確的。
另外,不同于 TCP,QUIC 的擁塞控制算法是可插拔的,由于其實(shí)現(xiàn)在用戶(hù)態(tài),服務(wù)可以根據(jù)不同的業(yè)務(wù),甚至不同的連接靈活選擇使用不同的擁塞控制算法。(Reno、New Reno、Cubic、BBR 等算法都有自己適合的場(chǎng)景)
2.5 QUIC 的兩級(jí)流量控制
很多人搞不清楚流量控制與擁塞控制的區(qū)別。二者有本質(zhì)上的區(qū)別。
流量控制要解決的問(wèn)題是:接收方控制發(fā)送方的數(shù)據(jù)發(fā)送的速度,就是我的接收能力就那么大點(diǎn),你別發(fā)太快了,你發(fā)太快了我承受不住,會(huì)給你丟掉 你還得重新發(fā)。
擁塞控制要解決的問(wèn)題是:數(shù)據(jù)在網(wǎng)絡(luò)的傳輸過(guò)程中,是否網(wǎng)絡(luò)有擁塞,是否有丟包,是否有亂序等問(wèn)題。如果中間傳輸?shù)臅r(shí)候網(wǎng)絡(luò)特別卡,數(shù)據(jù)包丟在中間了,發(fā)送方就需要重傳,那么怎么判斷是否擁塞了,重傳要怎么重傳法,按照什么算法進(jìn)行發(fā)送數(shù)據(jù)才能盡可能避免數(shù)據(jù)包在中間路徑丟掉,這是擁塞控制的核心。
所以,流量控制解決的是接收方的接收能力問(wèn)題,一般采用滑動(dòng)窗口算法;擁塞控制要解決的是中間傳輸?shù)臅r(shí)候網(wǎng)絡(luò)是否擁堵的問(wèn)題,一般采用慢啟動(dòng)、擁塞避免、擁塞恢復(fù)、快速重傳等算法。
圖 18-QUIC 流量控制
QUIC 是雙級(jí)流控,不僅有連接這一個(gè)級(jí)別的流控,還有流這個(gè)級(jí)別的流控。如下圖所示,每個(gè)流都有自己的可用窗口,可用窗口的大小取決于最大窗口數(shù)減去發(fā)送出去的最大偏移數(shù),跟中間已經(jīng)發(fā)送出去的數(shù)據(jù)包,是否按順序收到了對(duì)端的 ACK 無(wú)關(guān)。
QUIC 協(xié)議如何優(yōu)化
QUIC 協(xié)議定義了很多優(yōu)秀的功能,但是在實(shí)現(xiàn)的過(guò)程中,我們會(huì)遇到很多問(wèn)題導(dǎo)致無(wú)法達(dá)到預(yù)期的性能,比如 0-RTT 率很低,連接遷移失敗率很高等等。
3.1 QUIC 的 0-RTT 成功率不高
導(dǎo)致 0-RTT 成功率不高的原因一般有如下幾個(gè):
1. 服務(wù)端一般都是集群,對(duì)于客戶(hù)端來(lái)說(shuō),處理請(qǐng)求的服務(wù)端是不固定的,新的請(qǐng)求到來(lái)時(shí),如果當(dāng)前 client 沒(méi)有請(qǐng)求過(guò)該服務(wù)器,則服務(wù)器上沒(méi)有相關(guān)會(huì)話(huà)信息,會(huì)把該請(qǐng)求當(dāng)做一個(gè)新的連接來(lái)處理,重新走 1-RTT。
針對(duì)此種情況,我們可以考慮集群中所有的服務(wù)器使用相同的 ticket 文件。
2. 客戶(hù)端 IP 不是固定的,在發(fā)生連接遷移時(shí),服務(wù)端下發(fā)的 token 融合了客戶(hù)端的 IP,這個(gè) IP 變化了的話(huà),攜帶 token 服務(wù)端校驗(yàn)不過(guò),0-RTT 會(huì)失敗。
針對(duì)這個(gè)問(wèn)題,我們可以考慮采用如圖 19 所示的方法,使用設(shè)備信息或者 APP 信息來(lái)生成 token,唯一標(biāo)識(shí)一個(gè)客戶(hù)端。
圖 19- 使用設(shè)備信息提高 0-RTT 的成功率
3. Session Ticket 過(guò)期時(shí)間默認(rèn)是 2 天,超過(guò) 2 天后就會(huì)導(dǎo)致 0-RTT 失敗,然后降級(jí)走 1-RTT??梢钥紤]增長(zhǎng)過(guò)期時(shí)間。
3.2 實(shí)現(xiàn)連接遷移并不容易。
連接遷移的實(shí)現(xiàn),不可避開(kāi)的兩個(gè)問(wèn)題:一個(gè)是四層負(fù)載均衡器對(duì)連接遷移的影響,一個(gè)是七層負(fù)載均衡器對(duì)連接遷移的影響。
四層負(fù)載均衡器的影響:LVS、DPVS 等四層負(fù)載均衡工具基于四元組進(jìn)行轉(zhuǎn)發(fā),當(dāng)連接遷移發(fā)生時(shí),四元組會(huì)發(fā)生變化,該組件就會(huì)把同一個(gè)請(qǐng)求的數(shù)據(jù)包發(fā)送到不同的后端服務(wù)器上,導(dǎo)致連接遷移失敗;
七層負(fù)載均衡器的影響(QUIC 服務(wù)器多核的影響):由于多核的影響,一般服務(wù)器會(huì)有多個(gè) QUIC 服務(wù)端進(jìn)程,每個(gè)進(jìn)程負(fù)載處理不同的連接。內(nèi)核收到數(shù)據(jù)包后,會(huì)根據(jù)二元組(源 IP、源 port)選擇已經(jīng)存在的連接,并把數(shù)據(jù)包交給對(duì)應(yīng)的 socket。在連接遷移發(fā)生時(shí),源地址發(fā)生改變,可能會(huì)讓接下來(lái)的數(shù)據(jù)包去到不同的進(jìn)程,影響 socket 數(shù)據(jù)的接收。
如何解決以上兩個(gè)問(wèn)題?DPVS 要想支持 QUIC 的連接遷移,就不能再以四元組進(jìn)行轉(zhuǎn)發(fā),需要以連接 ID 進(jìn)行轉(zhuǎn)發(fā),需要建立 連接 ID 與對(duì)應(yīng)的后端服務(wù)器的對(duì)應(yīng)關(guān)系;
QUIC 服務(wù)器也是一樣的,內(nèi)核就不能用四元組來(lái)進(jìn)行查找 socket,四元組查找不到時(shí),就必須使用連接 ID 進(jìn)行查找 socket。但是內(nèi)核代碼又不能去修改(不可能去更新所有服務(wù)器的內(nèi)核版本),那么我們可以使用 eBPF 的方法進(jìn)行解決。如下圖 20 所示:
圖 20- 多核 QUIC 服務(wù)器解決連接遷移問(wèn)題
3.3 UDP 被限速或禁閉
業(yè)內(nèi)統(tǒng)計(jì)數(shù)據(jù)全球有 7% 地區(qū)的運(yùn)營(yíng)商對(duì) UDP 有限速或者禁閉,除了運(yùn)營(yíng)商還有很多企業(yè)、公共場(chǎng)合也會(huì)限制 UDP 流量甚至禁用 UDP。這對(duì)使用 UDP 來(lái)承載 QUIC 協(xié)議的場(chǎng)景會(huì)帶來(lái)致命的傷害。對(duì)此,我們可以采用多路競(jìng)速的方式使用 TCP 和 QUIC 同時(shí)建連。除了在建連進(jìn)行競(jìng)速以外,還可以對(duì)網(wǎng)絡(luò) QUIC 和 TCP 的傳輸延時(shí)進(jìn)行實(shí)時(shí)監(jiān)控和對(duì)比,如果有鏈路對(duì) UDP 進(jìn)行了限速,可以動(dòng)態(tài)從 QUIC 切換到 TCP。
圖 21-QUIC 和 TCP 協(xié)議競(jìng)速
3.4 QUIC 對(duì) CPU 消耗大
相對(duì)于 TCP,為什么 QUIC 更消耗資源?
QUIC 在用戶(hù)態(tài)實(shí)現(xiàn),需要更多的內(nèi)核空間與用戶(hù)空間的數(shù)據(jù)拷貝和上下文切換;
QUIC 的 ACK 報(bào)文也是加密的,TCP 是明文的。
內(nèi)核知道 TCP 連接的狀態(tài),不用為每一個(gè)數(shù)據(jù)包去做諸如查找目的路由、防火墻規(guī)則等操作,只需要在 tcp 連接建立的時(shí)候做一次即可,然而 QUIC 不行;
總的來(lái)說(shuō),QUIC 服務(wù)端消耗 CPU 的地方主要有三個(gè):密碼算法的開(kāi)銷(xiāo);udp 收發(fā)包的開(kāi)銷(xiāo);協(xié)議棧的開(kāi)銷(xiāo);
針對(duì)這些,我們可以適當(dāng)采取優(yōu)化措施來(lái):
使用 Intel 硬件加速卡卸載 TLS 握手
開(kāi)啟 GSO 功能。
數(shù)據(jù)在傳輸過(guò)程中,可以將一輪中所有的 ACK 解析后再同時(shí)進(jìn)行處理,避免處理大量的 ACK。
適當(dāng)將 QUIC 的包長(zhǎng)限制調(diào)高(比如從默認(rèn)的 1200 調(diào)到 1400 個(gè)字節(jié))
減少協(xié)議棧的內(nèi)存拷貝
QUIC 的性能
從公開(kāi)的數(shù)據(jù)來(lái)看,國(guó)內(nèi)各個(gè)廠(chǎng)(騰訊、阿里、字節(jié)、華為、OPPO、網(wǎng)易等等)使用了 QUIC 協(xié)議后,都有很大的提升,比如網(wǎng)易上了 QUIC 后,響應(yīng)速度提升 45%,請(qǐng)求錯(cuò)誤率降低 50%;比如字節(jié)火山引擎使用 QUIC 后,建連耗時(shí)降低 20%~30%;比如騰訊使用 QUIC 后,在騰訊會(huì)議、直播、游戲等場(chǎng)景耗時(shí)也降低 30%;
圖 22- 字節(jié)火山引擎 QUIC 業(yè)務(wù)收益
總 結(jié)
QUIC 協(xié)議的出現(xiàn),為 HTTP/3 奠定了基礎(chǔ)。這是近些年在 web 協(xié)議上最大的變革,也是最優(yōu)秀的一次實(shí)踐。面對(duì)新的協(xié)議,我們總是有著各種各樣的擔(dān)憂(yōu),誠(chéng)然,QUIC 協(xié)議在穩(wěn)定性上在成熟度上,的確還不如 TCP 協(xié)議,但是經(jīng)過(guò)近幾年的發(fā)展,成熟度已經(jīng)相當(dāng)不錯(cuò)了,Nginx 近期也發(fā)布了 1.25.0 版本,支持了 QUIC 協(xié)議。所以面對(duì)這樣優(yōu)秀的協(xié)議,我們希望更多的公司,更多的業(yè)務(wù)參與進(jìn)來(lái)使用 QUIC,推動(dòng) QUIC 更好的發(fā)展,推動(dòng)用戶(hù)上網(wǎng)速度更快!
-
網(wǎng)絡(luò)協(xié)議
+關(guān)注
關(guān)注
3文章
266瀏覽量
21545 -
HTTP
+關(guān)注
關(guān)注
0文章
505瀏覽量
31242 -
網(wǎng)絡(luò)通信
+關(guān)注
關(guān)注
4文章
801瀏覽量
29814 -
Quic
+關(guān)注
關(guān)注
0文章
25瀏覽量
7302
原文標(biāo)題:一文讀懂 QUIC 協(xié)議:更快、更穩(wěn)、更高效的網(wǎng)絡(luò)通信
文章出處:【微信號(hào):AI前線(xiàn),微信公眾號(hào):AI前線(xiàn)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論