1 HTTP 3.0
1.1 簡介
1.1.1 引言
從 HTTP/1.1
到 HTTP/2
狡相,HTTP
協(xié)議一直都是使用 TCP
作為傳輸協(xié)議定铜。
然而伞芹,就在最新的 HTTP/3
幌甘,HTTP
就直接把 TCP
拋棄了蔚万,向孤立無援的 UDP
伸出了援手驴剔,基于 UDP
協(xié)議的基礎(chǔ)上届巩,在應(yīng)用層實(shí)現(xiàn)了一個(gè)可靠的傳輸協(xié)議 —— QUIC
旱易。
很多同學(xué)可能就好奇了峦朗,HTTP
都用 TCP
都用了幾十年了建丧,而且 TCP
已經(jīng)是那么完善的可靠傳輸協(xié)議了,又有超時(shí)重傳波势、按序接收翎朱、流量控制、擁塞控制這些特性尺铣,怎么突然就把 TCP
拋棄了拴曲?到底是 TCP
哪里做的不夠好?
1.1.2 QUIC 協(xié)議概覽
QUIC
(Quick UDP Internet Connections
, 快速UDP網(wǎng)絡(luò)連接)是基于UDP
的協(xié)議, 利用了UDP
的速度和效率, 同時(shí)整合TCP
, TLS
和HTTP/2
的優(yōu)點(diǎn)并加以優(yōu)化. 用一張圖可以清晰的表示他們之間的關(guān)系.
QUIC
是用來替代TCP, SSL/TLS
的傳輸層協(xié)議, 在傳輸層之上還有應(yīng)用層
我們熟知的應(yīng)用層協(xié)議有HTTP, FTP, IMAP
等, 這些協(xié)議理論上都可以運(yùn)行在QUIC
上, 其中運(yùn)行在QUIC
之上的協(xié)議被稱為HTTP/3
凛忿, 這就是HTTP over QUIC
即HTTP/3
的含義
1.2 隊(duì)頭阻塞/多路復(fù)用問題
HTTP/1.1
和HTTP/2
都存在隊(duì)頭阻塞的問題(Head Of Line blocking
)
TCP
是個(gè)面向連接的協(xié)議, 即發(fā)送請(qǐng)求后需要收到ACK
消息, 以確認(rèn)對(duì)象已接受數(shù)據(jù). 如果每次請(qǐng)求都要在收到上次請(qǐng)求的ACK
消息后再請(qǐng)求, 那么效率無疑很低. 后來HTTP/1.1
提出了Pipeline
技術(shù), 允許一個(gè)TCP
連接同時(shí)發(fā)送多個(gè)請(qǐng)求. 這樣就提升了傳輸效率.
在這樣的背景下, 隊(duì)頭阻塞發(fā)生了. 比如, 一個(gè)
TCP
連接同時(shí)傳輸10個(gè)請(qǐng)求, 其中1,2,3個(gè)請(qǐng)求給客戶端接收, 但是第四個(gè)請(qǐng)求丟失, 那么后面第5-10個(gè)請(qǐng)求都被阻塞. 需要等第四個(gè)請(qǐng)求處理完畢后才能被處理. 這樣就浪費(fèi)了帶寬資源.
因此, HTTP
一般又允許每個(gè)主機(jī)建立6個(gè)TCP
連接, 這樣可以更加充分的利用帶寬資源, 但每個(gè)連接中隊(duì)頭阻塞的問題還是存在的.
TCP
隊(duì)頭阻塞的問題要從兩個(gè)角度看澈灼,一個(gè)是發(fā)送窗口的隊(duì)頭阻塞
,另外一個(gè)是接收窗口的隊(duì)頭阻塞
1.2.1 發(fā)送窗口的隊(duì)頭阻塞
TCP
發(fā)送出去的數(shù)據(jù)店溢,都是需要按序確認(rèn)的叁熔,只有在數(shù)據(jù)都被按順序確認(rèn)完后,發(fā)送窗口才會(huì)往前滑動(dòng)床牧。舉個(gè)例子荣回,比如下圖的發(fā)送方把發(fā)送窗口內(nèi)的數(shù)據(jù)全部都發(fā)出去了,可用窗口的大小就為 0 了戈咳,表明可用窗口耗盡心软,在沒收到 ACK
確認(rèn)之前是無法繼續(xù)發(fā)送數(shù)據(jù)了革砸。
接著,當(dāng)發(fā)送方收到對(duì)第 32~36
字節(jié)的 ACK
確認(rèn)應(yīng)答后糯累,則滑動(dòng)窗口往左邊移動(dòng) 5 個(gè)字節(jié)算利,因?yàn)橛?5 個(gè)字節(jié)的數(shù)據(jù)被應(yīng)答確認(rèn),接下來第 52~56
字節(jié)又變成了可用窗口泳姐,那么后續(xù)也就可以發(fā)送 52~56
這 5 個(gè)字節(jié)的數(shù)據(jù)了效拭。
但是如果某個(gè)數(shù)據(jù)報(bào)文丟失或者其對(duì)應(yīng)的 ACK
報(bào)文在網(wǎng)絡(luò)中丟失,會(huì)導(dǎo)致發(fā)送方無法移動(dòng)發(fā)送窗口胖秒,這時(shí)就無法再發(fā)送新的數(shù)據(jù)缎患,只能超時(shí)重傳這個(gè)數(shù)據(jù)報(bào)文,直到收到這個(gè)重傳報(bào)文的 ACK
阎肝,發(fā)送窗口才會(huì)移動(dòng)挤渔,繼續(xù)后面的發(fā)送行為。
舉個(gè)例子风题,比如下圖判导,客戶端是發(fā)送方,服務(wù)器是接收方
客戶端發(fā)送了第 5~9
字節(jié)的數(shù)據(jù)沛硅,但是第 5
字節(jié)的 ACK
確認(rèn)報(bào)文在網(wǎng)絡(luò)中丟失了眼刃,那么即使客戶端收到第 6~9
字節(jié)的 ACK 確認(rèn)報(bào)文,發(fā)送窗口也不會(huì)往前移動(dòng)摇肌。
此時(shí)的第 5 字節(jié)相當(dāng)于隊(duì)頭
擂红,因?yàn)闆]有收到隊(duì)頭
的 ACK
確認(rèn)報(bào)文,導(dǎo)致發(fā)送窗口無法往前移動(dòng)围小,此時(shí)發(fā)送方就無法繼續(xù)發(fā)送后面的數(shù)據(jù)昵骤,相當(dāng)于按下了發(fā)送行為的暫停鍵,這就是發(fā)送窗口的隊(duì)頭阻塞問題
1.2.2 接收窗口的隊(duì)頭阻塞
接收方收到的數(shù)據(jù)范圍必須在接收窗口范圍內(nèi)肯适,如果收到超過接收窗口范圍的數(shù)據(jù)变秦,就會(huì)丟棄該數(shù)據(jù),比如下圖接收窗口的范圍是 32 ~ 51
字節(jié)疹娶,如果收到第 52
字節(jié)以上數(shù)據(jù)都會(huì)被丟棄
接收窗口什么時(shí)候才能滑動(dòng)伴栓?當(dāng)接收窗口收到有序數(shù)據(jù)時(shí)伦连,接收窗口才能往前滑動(dòng)雨饺,然后那些已經(jīng)接收并且被確認(rèn)的有序
數(shù)據(jù)就可以被應(yīng)用層讀取。
但是惑淳,當(dāng)接收窗口收到的數(shù)據(jù)不是有序的额港,比如收到第 33~40
字節(jié)的數(shù)據(jù),由于第 32 字節(jié)數(shù)據(jù)沒有收到歧焦, 接收窗口無法向前滑動(dòng)移斩,那么即使先收到第 33~40
字節(jié)的數(shù)據(jù)肚医,這些數(shù)據(jù)也無法被應(yīng)用層讀取的。只有當(dāng)發(fā)送方重傳了第 32 字節(jié)數(shù)據(jù)并且被接收方收到后向瓷,接收窗口才會(huì)往前滑動(dòng)肠套,然后應(yīng)用層才能從內(nèi)核讀取第 32~40
字節(jié)的數(shù)據(jù)
至此發(fā)送窗口和接收窗口的隊(duì)頭阻塞問題都說完了,這兩個(gè)問題的原因都是因?yàn)?TCP
必須按序處理數(shù)據(jù)猖任,也就是 TCP
層為了保證數(shù)據(jù)的有序性你稚,只有在處理完有序的數(shù)據(jù)后,滑動(dòng)窗口才能往前滑動(dòng)朱躺,否則就停留刁赖。
停留發(fā)送窗口
會(huì)使得發(fā)送方無法繼續(xù)發(fā)送數(shù)據(jù)。
停留接收窗口
會(huì)使得應(yīng)用層無法讀取新的數(shù)據(jù)长搀。
其實(shí)也不能怪 TCP
協(xié)議宇弛,它本來設(shè)計(jì)目的就是為了保證數(shù)據(jù)的有序性
1.2.3 HTTP/2 的隊(duì)頭阻塞
HTTP/2
多路復(fù)用解決了上述的隊(duì)頭阻塞問題。在HTTP/2
中, 每個(gè)請(qǐng)求都被拆分為多個(gè)Frame
通過一條TCP
連接同時(shí)被傳輸, 這樣即使一個(gè)請(qǐng)求被阻塞, 也不會(huì)影響其他的請(qǐng)求.
但是, HTTP/2
雖然可以解決請(qǐng)求這一粒度下的阻塞, 但HTTP/2
的基礎(chǔ)TCP
協(xié)議本身卻也存在隊(duì)頭阻塞的問題. HTTP/2
的每個(gè)請(qǐng)求都會(huì)被拆分成多個(gè)Frame
, 不同請(qǐng)求的Frame組
合成Stream
, Stream
是TCP
上的邏輯傳輸單元, 這樣HTTP/2
就達(dá)到了一條連接同時(shí)發(fā)送多個(gè)請(qǐng)求的目標(biāo), 其中Stram1
已經(jīng)正確送達(dá), Stram2
中的第三個(gè)Frame
丟失, TCP
處理數(shù)據(jù)是有嚴(yán)格的前后順序, 先發(fā)送的Frame
要先被處理, 這樣就會(huì)要求發(fā)送方重新發(fā)送第三個(gè)Frame
, Steam3和Steam4
雖然已到達(dá)但卻不能被處理, 那么這時(shí)整條鏈路都會(huì)被阻塞
HTTP/2
通過抽象出 Stream
的概念源请,實(shí)現(xiàn)了 HTTP
并發(fā)傳輸枪芒,一個(gè) Stream
就代表 HTTP/1.1
里的請(qǐng)求和響應(yīng)
在 HTTP/2
連接上,不同 Stream
的幀
是可以亂序發(fā)送的(因此可以并發(fā)不同的 Stream
)谁尸,因?yàn)槊總€(gè)幀的頭部會(huì)攜帶 Stream ID
信息病苗,所以接收端可以通過 Stream ID
有序組裝成 HTTP
消息,而同一 Stream
內(nèi)部的幀必須是嚴(yán)格有序的症汹。
但是 HTTP/2
多個(gè) Stream
請(qǐng)求都是在一條 TCP
連接上傳輸硫朦,這意味著多個(gè) Stream
共用同一個(gè) TCP
滑動(dòng)窗口,那么當(dāng)發(fā)生數(shù)據(jù)丟失背镇,滑動(dòng)窗口是無法往前移動(dòng)的咬展,此時(shí)就會(huì)阻塞住所有的 HTTP
請(qǐng)求,這屬于 TCP
層隊(duì)頭阻塞瞒斩。
不僅如此破婆,由于HTTP/2
必須使用HTTPS
, 而HTTPS
使用TLS
協(xié)議也存在隊(duì)頭阻塞問題. TLS
基于Record
組織數(shù)據(jù), 將一對(duì)數(shù)據(jù)放在一起加密, 加密完成后又拆分成多個(gè)TCP
包傳輸. 一般每個(gè)Record 16K
, 包含12個(gè)TCP包, 這樣如果12個(gè)TCP包中有任何一個(gè)包丟失, 那么整個(gè)Record
都無法解密
隊(duì)頭阻塞
會(huì)導(dǎo)致HTTP/2
在更容易丟包的弱網(wǎng)絡(luò)環(huán)境下比HTTP/1.1
更慢.
1.2.4 沒有隊(duì)頭阻塞的 QUIC
QUIC
解決隊(duì)頭阻塞的問題主要有兩點(diǎn):
-
QUIC
的傳輸單位是Packet
, 加密單元也是Packet
, 整個(gè)加密, 傳輸, 解密都基于Packet
, 這就能避免TLS
的阻塞問題. -
QUIC
基于UDP
,UDP
的數(shù)據(jù)包在接收端沒有處理順序, 即使中間丟失一個(gè)包, 也不會(huì)阻塞整條連接. 其他的資源會(huì)被正常處理.
QUIC
也借鑒 HTTP/2
里的 Stream
的概念,在一條 QUIC
連接上可以并發(fā)發(fā)送多個(gè) HTTP
請(qǐng)求 (Stream
)胸囱。但是 QUIC
給每一個(gè) Stream
都分配了一個(gè)獨(dú)立的滑動(dòng)窗口祷舀,這樣使得一個(gè)連接上的多個(gè) Stream
之間沒有依賴關(guān)系,都是相互獨(dú)立的烹笔,各自控制的滑動(dòng)窗口裳扯。
假如 Stream2
丟了一個(gè) UDP
包,也只會(huì)影響 Stream2
的處理谤职,不會(huì)影響其他 Stream
饰豺,與 HTTP/2
不同,HTTP/2
只要某個(gè)流中的數(shù)據(jù)包丟失了允蜈,其他流也會(huì)因此受影響冤吨。
1.3 TCP 建立連接的延遲
對(duì)于 HTTP/1
和 HTTP/2
協(xié)議蒿柳,TCP
和 TLS
是分層的,分別屬于內(nèi)核實(shí)現(xiàn)的傳輸層漩蟆、openssl
庫實(shí)現(xiàn)的表示層垒探,因此它們難以合并在一起,需要分批次來握手怠李,先 TCP
握手(1RTT)叛复,再 TLS
握手(2RTT),所以需要 3RTT 的延遲才能傳輸數(shù)據(jù)扔仓,就算 Session
會(huì)話服務(wù)褐奥,也需要至少 2 個(gè) RTT,這在一定程序上增加了數(shù)據(jù)傳輸?shù)难舆t翘簇。
RTT: round-trip time
, 僅包括請(qǐng)求訪問來回的時(shí)間
TCP
三次握手和 TLS
握手延遲撬码,如圖:
HTTP/3
在傳輸數(shù)據(jù)前雖然需要 QUIC
協(xié)議握手,這個(gè)握手過程只需要 1 RTT
版保,握手的目的是為確認(rèn)雙方的連接 ID
呜笑,連接遷移就是基于連接 ID
實(shí)現(xiàn)的。
但是 HTTP/3
的 QUIC
協(xié)議并不是與 TLS
分層彻犁,因?yàn)?QUIC
也是應(yīng)用層實(shí)現(xiàn)的協(xié)議叫胁,所以可以將 QUIC
和 TLS
協(xié)議握手的過程合并在一起,QUIC
內(nèi)部包含了 TLS
汞幢,它在自己的幀會(huì)攜帶 TLS
里的 記錄
驼鹅,再加上 QUIC
使用的是 TLS1.3,因此僅需 1 個(gè) RTT 就可以 同時(shí) 完成建立連接與密鑰協(xié)商森篷,甚至在第二次連接的時(shí)候输钩,應(yīng)用數(shù)據(jù)包可以和 QUIC
握手信息(連接信息 + TLS 信息)一起發(fā)送,達(dá)到 0-RTT 的效果仲智。
如下圖右邊部分买乃,HTTP/3
當(dāng)會(huì)話恢復(fù)時(shí),有效負(fù)載數(shù)據(jù)與第一個(gè)數(shù)據(jù)包一起發(fā)送钓辆,可以做到 0-RTT(下圖的右下角):
1.4 HTTP/3 連接過程
HTTP/3
首次連接只需要1RTT
, 后面的鏈接只需要0RTT
, 意味著客戶端發(fā)送給服務(wù)端的第一個(gè)包就帶有請(qǐng)求數(shù)據(jù), 其主要連接過程如下:
- 首次連接剪验,客戶端發(fā)送
Inchoate Client Hello
, 用于請(qǐng)求連接; - 服務(wù)端生成g, p, a, 根據(jù)g, p, a算出A, 然后將g, p, A放到
Server Config
中在發(fā)送Rejection
消息給客戶端. - 客戶端接收到
g,p,A
后, 自己再生成b
, 根據(jù)g,p,a
算出B
, 根據(jù)A,p,b
算出初始密鑰K
,B和K
算好后, 客戶端會(huì)用K加密HTTP數(shù)據(jù), 連同B一起發(fā)送給服務(wù)端. - 服務(wù)端接收到B后, 根據(jù)
a,p,B
生成與客戶端同樣的密鑰, 再用這密鑰解密收到的HTTP
數(shù)據(jù). 為了進(jìn)一步的安全(前向安全性), 服務(wù)端會(huì)更新自己的隨機(jī)數(shù)a和公鑰, 在生成新的密鑰S, 然后把公鑰通過Server Hello
發(fā)送給客戶端. 連同Server Hello
消息, 還有HTTP返回?cái)?shù)據(jù).
image.png
這里使用DH
密鑰交換算法, DH
算法的核心就是服務(wù)端生成a,g,p
3個(gè)隨機(jī)數(shù), a
自己持有, g和p
要傳輸給客戶端, 而客戶端會(huì)生成b
這 1個(gè)隨機(jī)數(shù), 通過DH
算法客戶端和服務(wù)端可以算出同樣的密鑰。在這過程中a和b
并不參與網(wǎng)絡(luò)傳輸, 安全性大大提升前联。因?yàn)?code>p和g是大數(shù), 所以即使在網(wǎng)絡(luò)傳輸中p, g, A, B
都被劫持, 靠現(xiàn)在的計(jì)算力算力也無法破解
1.5 連接遷移影響
基于 TCP
傳輸協(xié)議的 HTTP
協(xié)議功戚,由于是通過 四元組(源IP
、源端口
蛀恩、目的 IP
疫铜、目的端口
)確定一條 TCP
連接
那么當(dāng)移動(dòng)設(shè)備的網(wǎng)絡(luò)從 4G
切換到 WIFI
時(shí),意味著 IP
地址變化了双谆,那么就必須要斷開連接壳咕,然后重新建立 TCP
連接。
而建立連接的過程包含 TCP
三次握手和 TLS
四次握手的時(shí)延顽馋,以及 TCP
慢啟動(dòng)的減速過程谓厘,給用戶的感覺就是網(wǎng)絡(luò)突然卡頓了一下,因此連接的遷移成本是很高的寸谜。
QUIC
協(xié)議沒有用四元組
的方式來綁定
連接竟稳,而是通過 連接ID
來標(biāo)記通信的兩個(gè)端點(diǎn),客戶端和服務(wù)器可以各自選擇一組 ID
來標(biāo)記自己熊痴,因此即使移動(dòng)設(shè)備的網(wǎng)絡(luò)變化后他爸,導(dǎo)致 IP
地址變化了,只要仍保有上下文信息(比如連接 ID
果善、TLS 密鑰
等)诊笤,就可以 無縫 地復(fù)用原連接,消除重連的成本巾陕,沒有絲毫卡頓感讨跟,達(dá)到了連接遷移的功能。
1.6 擁塞控制影響
擁塞控制
的目的是避免過多的數(shù)據(jù)一下子涌入網(wǎng)絡(luò), 導(dǎo)致網(wǎng)絡(luò)超出最大負(fù)荷. QUIC
的擁塞控制與TCP
類似, 并在此基礎(chǔ)上做了改進(jìn). 先來看看TCP
的擁塞控制
-
慢啟動(dòng)
: 發(fā)送方向接收方發(fā)送一個(gè)單位的數(shù)據(jù), 收到確認(rèn)后發(fā)送2個(gè)單位, 然后是4個(gè), 8個(gè)依次指數(shù)增長, 這個(gè)過程中不斷試探網(wǎng)絡(luò)的擁塞程度. -
避免擁塞
: 指數(shù)增長到某個(gè)限制之后,指數(shù)增長
變?yōu)?code>線性增長 -
快速重傳
: 發(fā)送方每一次發(fā)送都會(huì)設(shè)置一個(gè)超時(shí)計(jì)時(shí)器, 超時(shí)后認(rèn)為丟失, 需要重發(fā) -
快速恢復(fù)
: 在上面快速重傳的基礎(chǔ)上, 發(fā)送方重新發(fā)送數(shù)據(jù)時(shí), 也會(huì)啟動(dòng)一個(gè)超時(shí)定時(shí)器, 如果收到確認(rèn)消息則進(jìn)入擁塞避免階段
, 如果仍然超時(shí), 則回到慢啟動(dòng)階段.
QUIC
重新實(shí)現(xiàn)了TCP
協(xié)議中的Cubic
算法進(jìn)行擁塞控制, 下面是QUIC
改進(jìn)的擁塞控制的特性:
- 熱插拔
TCP
中如果要修改擁塞控制策略, 需要在系統(tǒng)層面進(jìn)行那個(gè)操作,QUIC
修改擁塞控制策略只需要在應(yīng)用層操作, 并且QUIC
會(huì)根據(jù)不同的網(wǎng)絡(luò)環(huán)境, 用戶來動(dòng)態(tài)選擇擁塞控制算法. - 前向糾錯(cuò)
FEC
QUIC
使用前向糾錯(cuò)(FEC, Forword Error Correction
)技術(shù)增加協(xié)議的容錯(cuò)性. 一段數(shù)據(jù)被切分為10個(gè)包后, 一次對(duì)每個(gè)包進(jìn)行異或運(yùn)算, 運(yùn)算結(jié)果會(huì)作為FEC
包與數(shù)據(jù)包一起被傳輸, 如果傳輸過程中有一個(gè)數(shù)據(jù)包丟失, 那么就可以根據(jù)剩余9個(gè)包以及FEC
包推算出丟失的那個(gè)包的數(shù)據(jù), 這樣就大大增加了協(xié)議的容錯(cuò)性.
這是符合現(xiàn)階段網(wǎng)絡(luò)傳輸技術(shù)的一種方案, 現(xiàn)階段帶寬已經(jīng)不是網(wǎng)絡(luò)傳輸?shù)钠款i, 往返時(shí)間才是, 所以新的網(wǎng)絡(luò)傳輸協(xié)議可以適當(dāng)增加數(shù)據(jù)冗余, 減少重傳操作. - 單調(diào)遞增的
Packer Number
TCP
為了保證可靠性, 使用Sequence Number
和ACK
來確認(rèn)消息是否有序到達(dá), 但這樣的設(shè)計(jì)存在缺陷.
超時(shí)發(fā)生后客戶端發(fā)起重傳, 后來接受到了ACK
確認(rèn)消息, 但因?yàn)樵颊?qǐng)求和重傳請(qǐng)求接受到的ACK
消息一樣, 所以客戶端就不知道這個(gè)ACK
對(duì)應(yīng)的是原始請(qǐng)求還是重傳請(qǐng)求. 這就會(huì)造成歧義.
RTT: Round Trip Time
, 往返事件
RTO: Retransmission Timeout
, 超時(shí)重傳時(shí)間
如果客戶端認(rèn)為是重傳的ACK
, 但實(shí)際上是右圖的情形, 會(huì)導(dǎo)致RTT偏小, 反之會(huì)導(dǎo)致RTT偏大.
image.png
QUIC
解決了上面的的歧義問題, 與Sequence Number
不同, Packet Number
嚴(yán)格單調(diào)遞增, 如果Packet N
丟失了, 那么重傳時(shí)Packet
的標(biāo)識(shí)就不會(huì)是N, 而是比N大的數(shù)字, 比如N+M, 這樣發(fā)送方接收到確認(rèn)消息時(shí), 就能方便的知道ACK對(duì)應(yīng)的原始請(qǐng)求還是重傳請(qǐng)求.
-
ACK Delay
TCP
計(jì)算RTT
時(shí)沒有考慮接收方接受到數(shù)據(jù)發(fā)發(fā)送方確認(rèn)消息之間的延遲, 如下圖所示, 這段延遲即ACK Delay
鄙煤。QUIC
考慮了這段延遲, 使得RTT的計(jì)算更加準(zhǔn)確.
image.png 更多的ACK塊
一般來說, 接收方收到發(fā)送方的消息后都應(yīng)該發(fā)送一個(gè)ACK
恢復(fù), 表示收到了數(shù)據(jù). 但每收到一個(gè)數(shù)據(jù)就返回一個(gè)ACK
恢復(fù)實(shí)在太麻煩, 所以一般不會(huì)立即回復(fù), 而是接受到多個(gè)數(shù)據(jù)后再回復(fù),TCP SACK
最多提供3個(gè)ACK block
. 但在有些場景下, 比如下載, 只需要服務(wù)器返回?cái)?shù)據(jù)就好, 但按照TCP
的設(shè)計(jì), 每收到三個(gè)數(shù)據(jù)包就要返回一個(gè)ACK
, 而QUIC
最多可以捎帶256個(gè)ACK block
, 在丟包率比較嚴(yán)重的網(wǎng)絡(luò)下, 更多的ACK
可以減少重傳量, 提升網(wǎng)絡(luò)效率
1.7 流量控制影響
TCP
會(huì)對(duì)每個(gè)TCP
連接進(jìn)行流量控制, 流量控制的意思是讓發(fā)送方不要發(fā)送太快, 要讓接收方來得及接受, 不然會(huì)導(dǎo)致數(shù)據(jù)溢出而丟失, TCP
的流量控制主要通過滑動(dòng)窗口來實(shí)現(xiàn)的. 可以看到, 擁塞控制主要是控制發(fā)送方的發(fā)送策略, 但沒有考慮接收方的接收能力, 流量控制則是對(duì)接收方接收能力的一種補(bǔ)充
QUIC
只需要建立一條連接, 在這條連接上同時(shí)傳輸多條Stream
, 好比有一條道路, 兩邊都分別有一個(gè)倉庫, 道路中有很多車輛運(yùn)送物資. QUIC
的流量控制有兩個(gè)級(jí)別: 連接級(jí)別(Connection Level)和Stream 級(jí)別(Stream Level)
.
對(duì)于單條的Stream
的流量控制: Stream
還沒傳輸數(shù)據(jù)時(shí), 接收窗口(flow control recevice window
)就是最大接收窗口, 隨著接收方接收到數(shù)據(jù)后, 接收窗口不斷縮小. 在接收到的數(shù)據(jù)中, 有的數(shù)據(jù)已被處理, 而有的數(shù)據(jù)還沒來得及處理. 如下圖, 藍(lán)色塊表示已處理數(shù)據(jù), 黃色塊表示違背處理數(shù)據(jù), 這部分?jǐn)?shù)據(jù)的到來, 使得Stream的接收窗口縮小.
隨著數(shù)據(jù)不斷被處理, 接收方就有能力處理更多數(shù)據(jù). 當(dāng)滿足 (flow control receivce offset - consumed bytes) < (max receive window/2)
時(shí), 接收方會(huì)發(fā)送WINDOW_UPDATE frame
告訴發(fā)送方你可以再多發(fā)送數(shù)據(jù), 這時(shí)候flow control receive offset
就會(huì)偏移, 接收窗口增大, 發(fā)送方可以發(fā)送更多數(shù)據(jù)到接收方.
Stream
級(jí)別對(duì)防止接收端接收過多數(shù)據(jù)作用有限, 更需要借助Connection
級(jí)別的流量控制. 理解了Stream
流量那么也很好理解Connection的流控.
在Stream
中,接收窗口=最大接受窗口 - 已接收數(shù)據(jù)
而對(duì)于Connection來說:接收窗口 = Stream1 接收窗口 + Stream2 接收窗口 + ... + StreamN 接收窗口
1.8 升級(jí) TCP 的工作很困難
TCP
協(xié)議是誕生在 1973 年晾匠,至今 TCP
協(xié)議依然還在實(shí)現(xiàn)更多的新特性。
但是 TCP
協(xié)議是在內(nèi)核中實(shí)現(xiàn)的梯刚,應(yīng)用程序只能使用不能修改凉馆,如果要想升級(jí) TCP
協(xié)議,那么只能升級(jí)內(nèi)核亡资。
而升級(jí)內(nèi)核這個(gè)工作是很麻煩的事情句喜,麻煩的事情不是說升級(jí)內(nèi)核這個(gè)操作很麻煩,而是由于內(nèi)核升級(jí)涉及到底層軟件和運(yùn)行庫的更新沟于,我們的服務(wù)程序就需要回歸測試是否兼容新的內(nèi)核版本咳胃,所以服務(wù)器的內(nèi)核升級(jí)也比較保守和緩慢。
很多 TCP
協(xié)議的新特性旷太,都是需要客戶端和服務(wù)端同時(shí)支持才能生效的展懈,比如 TCP Fast Open
這個(gè)特性,雖然在2013 年就被提出了供璧,但是 Windows
很多系統(tǒng)版本依然不支持它存崖,這是因?yàn)?PC
端的系統(tǒng)升級(jí)滯后很嚴(yán)重,Windows Xp
現(xiàn)在還有大量用戶在使用睡毒,盡管它已經(jīng)存在快 20 年来惧。
所以,即使 TCP
有比較好的特性更新演顾,也很難快速推廣供搀,用戶往往要幾年或者十年才能體驗(yàn)到隅居。
相反,QUIC
是處于應(yīng)用層的葛虐,所以如果升級(jí) QUIC
協(xié)議的話胎源,其實(shí)就是像升級(jí)軟件一樣輕松。而且屿脐,QUIC
可以針對(duì)不同的應(yīng)用設(shè)置不同的擁塞控制算法涕蚤,這樣靈活性就很高了,這是 TCP
做不到的的诵,因?yàn)?TCP
更改擁塞控制算法是對(duì)系統(tǒng)中所有應(yīng)用都生效万栅,無法根據(jù)不同應(yīng)用設(shè)定不同的擁塞控制策略
1.9 總結(jié)
HTTP/3
拋棄 TCP
后,基于 UDP
實(shí)現(xiàn)的可靠傳輸 QUIC
協(xié)議西疤,帶來這四點(diǎn)好處:
- 降低連接耗時(shí):在客戶端有緩存的情況下實(shí)現(xiàn)0-RTT建立連接
- 更靈活的擁塞控制:在用戶態(tài)可以為每個(gè)請(qǐng)求配置不同的擁塞控制策略
- 無隊(duì)頭阻塞的多路復(fù)用:每個(gè)請(qǐng)求流獨(dú)立擁有滑動(dòng)窗口烦粒,互不影響
- 連接遷移:網(wǎng)絡(luò)切換不會(huì)中斷數(shù)據(jù)傳輸
不過, HTTP/3
也面臨了一些挑戰(zhàn)瘪阁,QUIC
基于 UDP
協(xié)議在用戶空間實(shí)現(xiàn)的可靠傳輸協(xié)議撒遣,如果一些網(wǎng)絡(luò)設(shè)備無法識(shí)別出 QUIC
協(xié)議,那么在這些網(wǎng)絡(luò)設(shè)備的眼里它就是一個(gè) UDP
協(xié)議管跺。
而幾乎所有的電信運(yùn)營商都會(huì) 歧視 UDP
數(shù)據(jù)包义黎,原因也很容易理解,畢竟歷史上幾次臭名昭著的 DDoS
(分布式拒絕服務(wù)攻擊) 攻擊都是基于 UDP
的豁跑。國內(nèi)運(yùn)營商即使沒有封禁 UDP
廉涕,也是對(duì) UDP
進(jìn)行嚴(yán)格限流的。
自 2013 年 QUIC
被正式公開以來艇拍,到 2023 年已經(jīng)發(fā)展了差不多 10 年狐蜕,目前網(wǎng)上已經(jīng)有了不少熱門開源的項(xiàng)目,除去帶頭大哥 Google 在完成了對(duì)自身搜索引擎的支持卸夕,還同時(shí)拉上了 Gmail 层释、YouTube 等站點(diǎn)。但對(duì)于國內(nèi)的絕大部分站點(diǎn)來說快集,大部分還是 HTTP/2 協(xié)議贡羔,HTTP/3 之路,似乎還停留在東土大唐