說(shuō)一下 HTTP/3 新特性,為什么選擇使用 UDP 協(xié)議?
本文轉(zhuǎn)載自微信公眾號(hào)「三分鐘學(xué)前端」,作者sisterAn 。轉(zhuǎn)載本文請(qǐng)聯(lián)系三分鐘學(xué)前端公眾號(hào)。
引言
本文主要分為以下幾個(gè)方面循序漸進(jìn)走進(jìn) HTTP/3:
- HTTP/2 和 TCP 的致命缺陷
- QUIC 協(xié)議為什么選擇 UDP
- QUIC 和 HTTP/3 新特性
- QUIC 和 HTTP/3 前景發(fā)展展望
HTTP/2 和 TCP 的缺陷
HTTP/2 使用二進(jìn)制傳輸、Header 壓縮(HPACK)、多路復(fù)用等,相較于 HTTP/1.1 大幅提高了數(shù)據(jù)傳輸效率,但它仍然存在著以下幾個(gè)致命問(wèn)題(主要由底層支撐的 TCP 協(xié)議造成):
- 建立連接時(shí)間長(zhǎng)
- 隊(duì)頭阻塞問(wèn)題相較于 HTTP/1.1 更嚴(yán)重
1. 建立連接時(shí)間長(zhǎng)
RTT 往返時(shí)間
如何定義建立連接時(shí)間喃?這里引入一個(gè)概念:RTT(Round-Trip Time),往返時(shí)間,表示從發(fā)送端發(fā)送數(shù)據(jù)開始,到發(fā)送端收到來(lái)自接收端的確認(rèn)(接收端收到數(shù)據(jù)后便立即發(fā)送確認(rèn),不包含數(shù)據(jù)傳輸時(shí)間)總共經(jīng)歷的時(shí)間,即通信一來(lái)一回的時(shí)間
TCP 建立連接時(shí)間
TCP 通過(guò)三次揮手建立了 TCP 虛擬通道,它總共需要花費(fèi):
- 一去 (SYN):客戶端向服務(wù)端發(fā)送連接請(qǐng)求報(bào)文段。該報(bào)文段中包含自身的數(shù)據(jù)通訊初始序號(hào)。請(qǐng)求發(fā)送后,客戶端便進(jìn)入 SYN-SENT 狀態(tài)
- 二回 (SYN+ACK):服務(wù)端收到連接請(qǐng)求報(bào)文段后,如果同意連接,則會(huì)發(fā)送一個(gè)應(yīng)答,該應(yīng)答中也會(huì)包含自身的數(shù)據(jù)通訊初始序號(hào),發(fā)送完成后便進(jìn)入 SYN-RECEIVED 狀態(tài)
- 三去 (ACK):當(dāng)客戶端收到連接同意的應(yīng)答后,還要向服務(wù)端發(fā)送一個(gè)確認(rèn)報(bào)文??蛻舳税l(fā)完這個(gè)報(bào)文段后便進(jìn)入 ESTABLISHED 狀態(tài),服務(wù)端收到這個(gè)應(yīng)答后也進(jìn)入 ESTABLISHED 狀態(tài),此時(shí)連接建立成功
相當(dāng)于一個(gè)半來(lái)回,故 TCP 建立連接時(shí)間 = 1.5 RTT
HTTP 交易時(shí)間
客戶端在請(qǐng)求數(shù)據(jù)的時(shí)候,首先花費(fèi) 1.5 RTT 建立 TCP 連接,然后 TCP 才開始傳輸 HTTP 請(qǐng)求,瀏覽器收到服務(wù)器的響應(yīng),又要等待的時(shí)間為:
- 一去(HTTP Request)
- 二回 (HTTP Responses)
故 HTTP 交易時(shí)間 = 1 RTT
由于 TCP 在第三次握手的時(shí)候,不需要等待服務(wù)器端的響應(yīng),所以節(jié)省 0.5 RTT,那么基于 TCP 傳輸?shù)?HTTP 通信,一共花費(fèi)的時(shí)間總和:
HTTP 通信時(shí)間總和 = TCP 建立連接時(shí)間 + HTTP 交易時(shí)間 = 1 RTT + 1 RTT = 2 RTT
HTTPS 通信時(shí)間
HTTP/2 延續(xù)了 HTTP/1 的“明文”特點(diǎn),可以像以前一樣使用明文傳輸數(shù)據(jù),不強(qiáng)制使用加密通信,但 HTTPS 已經(jīng)是大勢(shì)所趨,各大主流瀏覽器都公開宣布只支持加密的 HTTP/2,所以,真實(shí)應(yīng)用中的 HTTP/2 是還是加密的:
HTTPS 其實(shí)是 HTTP+SSL/TLS 的簡(jiǎn)稱
所以,HTTPS 通信時(shí)間 = TCP建立連接時(shí)間 + TLS 連接時(shí)間 + HTTP交易時(shí)間
TLS 連接時(shí)間
在 TLS 1.2 協(xié)議的握手,需要 2 個(gè) RTT:
- 一去:客戶端發(fā)送一個(gè)隨機(jī)數(shù) C,客戶端的 TLS 版本號(hào)以及支持的密碼套件列表給服務(wù)器端
- 二回:服務(wù)端收到客戶端的隨機(jī)值,自己也產(chǎn)生一個(gè)隨機(jī)值 S ,并根據(jù)客戶端需求的協(xié)議和加密方式來(lái)使用對(duì)應(yīng)的方式,并且發(fā)送自己的證書(如果需要驗(yàn)證客戶端證書需要說(shuō)明)
- 三去:客戶端收到服務(wù)端的證書并驗(yàn)證是否有效,驗(yàn)證通過(guò)會(huì)再生成一個(gè)隨機(jī)值 pre-master,通過(guò)服務(wù)端證書的公鑰去加密這個(gè)隨機(jī)值并發(fā)送給服務(wù)端。如果服務(wù)端需要驗(yàn)證客戶端證書的話會(huì)附帶證書(雙向認(rèn)證,比如網(wǎng)上銀行用 U 盾)
- 四回: 服務(wù)端收到加密過(guò)的隨機(jī)值并使用私鑰解密獲得第三個(gè)隨機(jī)值,這時(shí)候兩端都擁有了三個(gè)隨機(jī)值,可以通過(guò)這三個(gè)隨機(jī)值(C/S 加 pre-master 算出主密鑰)按照之前約定的加密方式生成密鑰,接下來(lái)的通信就可以通過(guò)該會(huì)話密鑰來(lái)加密解密
HTTPS 通信時(shí)間總和 = TCP 建立連接時(shí)間 + TLS 連接時(shí)間 + HTTP交易時(shí)間 = 1 RTT + 2 RTT + 1 RTT = 4 RTT
如果服務(wù)器距離客戶端很近,RTT 時(shí)間較短 < 10ms,那么 HTTPS 通信時(shí)間也不會(huì)超過(guò) 40 ms,用戶不會(huì)感知,但如果距離較遠(yuǎn),相隔上萬(wàn)公里,一個(gè) RTT 時(shí)間通常在200ms以上,那么 HTTPS 通信將花費(fèi) 800ms 甚至 1s 以上,這就嚴(yán)重影響到用戶體驗(yàn)了
注意:在 TLS 1.3 協(xié)議中,首次建立連接只需要一個(gè) RTT,后面恢復(fù)連接不需要 RTT 了
HTTPS 通信時(shí)間總和(基于TLS1.2) = TCP 建立連接時(shí)間 + TLS1.2 連接時(shí)間 + HTTP交易時(shí)間 = 1 RTT + 2 RTT + 1 RTT = 4 RTT
HTTPS 通信時(shí)間總和(基于TLS1.3) = TCP 建立連接時(shí)間 + TLS1.3 連接時(shí)間 + HTTP交易時(shí)間 = 1 RTT + 1 RTT + 1 RTT = 3 RTT
2. 隊(duì)頭阻塞問(wèn)題相較于 HTTP/1.1 更嚴(yán)重
因?yàn)?HTTP/2 使用了多路復(fù)用,一般來(lái)說(shuō)同一域名下只需要使用一個(gè) TCP 連接。當(dāng)這個(gè)連接中出現(xiàn)了丟包的情況,那就會(huì)導(dǎo)致 HTTP/2 的表現(xiàn)情況反倒不如 HTTP/1 了。
因?yàn)樵诔霈F(xiàn)丟包的情況下,整個(gè) TCP 都要開始等待重傳,也就導(dǎo)致了后面的所有數(shù)據(jù)都被阻塞了。但是對(duì)于 HTTP/1 來(lái)說(shuō),可以開啟多個(gè) TCP 連接,出現(xiàn)這種情況反到只會(huì)影響其中一個(gè)連接,剩余的 TCP 連接還可以正常傳輸數(shù)據(jù)。
QUIC 協(xié)議為什么選擇 UDP
那么可能就會(huì)有人考慮到去修改 TCP 協(xié)議,其實(shí)這已經(jīng)是一件不可能完成的任務(wù)了。因?yàn)?TCP 存在的時(shí)間實(shí)在太長(zhǎng),已經(jīng)充斥在各種設(shè)備中,并且這個(gè)協(xié)議是由操作系統(tǒng)實(shí)現(xiàn)的,更新起來(lái)不大現(xiàn)實(shí)。
基于這個(gè)原因,Google 就更起爐灶搞了一個(gè)基于 UDP 協(xié)議的 QUIC 協(xié)議
谷歌這樣做看似出乎意料的,但我們對(duì)比一下 TCP 與 UDP 就會(huì)發(fā)現(xiàn),這是很有道理的:
- 基于 TCP 開發(fā)的設(shè)備和協(xié)議非常多,兼容困難
- TCP 協(xié)議棧是 Linux 內(nèi)部的重要部分,修改和升級(jí)成本很大
- UDP 本身是無(wú)連接的、沒(méi)有建鏈和拆鏈成本
- UDP 的數(shù)據(jù)包無(wú)隊(duì)頭阻塞問(wèn)題
- UDP 改造成本小
從上面的對(duì)比可以知道,谷歌要想從 TCP 上進(jìn)行改造升級(jí)絕非易事,但是 UDP 雖然沒(méi)有 TCP 為了保證可靠連接而引發(fā)的問(wèn)題,但是 UDP 本身不可靠,又不能直接用。
所以,谷歌決定在 UDP 基礎(chǔ)上改造一個(gè)具備 TCP 協(xié)議優(yōu)點(diǎn)的新協(xié)議也就順理成章了,這個(gè)新協(xié)議就是 QUIC 協(xié)議(Quick UDP Internet Connection),并且使用在了 HTTP/3 上,當(dāng)然 HTTP/3 之前名為 HTTP-over-QUIC,從這個(gè)名字中我們也可以發(fā)現(xiàn),HTTP/3 最大的改造就是使用了 QUIC
QUIC 和 HTTP/3 新特性
QUIC 雖然基于 UDP,但是在原本的基礎(chǔ)上新增了很多功能,比如多路復(fù)用、0-RTT、使用 TLS1.3 加密、流量控制、有序交付、重傳等等功能。這里我們就挑選幾個(gè)重要的功能學(xué)習(xí)下這個(gè)協(xié)議的內(nèi)容。
1. 多路復(fù)用,解決隊(duì)頭阻塞問(wèn)題
雖然 HTTP/2 支持了多路復(fù)用,但是 TCP 協(xié)議終究是沒(méi)有這個(gè)功能的。QUIC 原生就實(shí)現(xiàn)了這個(gè)功能
QUIC 協(xié)議是基于 UDP 協(xié)議實(shí)現(xiàn)的,同一個(gè) QUIC 連接上可以創(chuàng)建多個(gè) stream(數(shù)據(jù)流) 來(lái)發(fā)送多個(gè) HTTP 請(qǐng)求,并且,多個(gè) stream 之間沒(méi)有依賴,傳輸?shù)膯蝹€(gè) stream可以保證有序交付且不會(huì)影響其他的數(shù)據(jù)流
例如下圖,stream2 丟了一個(gè) UDP 包,不會(huì)影響后面跟著 Stream3 和 Stream4。這樣的技術(shù)就解決了之前 TCP 存在的隊(duì)頭阻塞問(wèn)題。
并且 QUIC 在移動(dòng)端的表現(xiàn)也會(huì)比 TCP 好。因?yàn)?TCP 是基于 IP 和端口去識(shí)別連接的,這種方式在多變的移動(dòng)端網(wǎng)絡(luò)環(huán)境下是很脆弱的。但是 QUIC 是通過(guò) ID 的方式去識(shí)別一個(gè)連接,不管你網(wǎng)絡(luò)環(huán)境如何變化,只要 ID 不變,就能迅速重連上。
2. 0RTT
通過(guò)使用類似 TCP 快速打開的技術(shù),緩存當(dāng)前會(huì)話的上下文,在下次恢復(fù)會(huì)話的時(shí)候,只需要將之前的緩存?zhèn)鬟f給服務(wù)端驗(yàn)證通過(guò)就可以進(jìn)行傳輸了。
0RTT 建連可以說(shuō)是 QUIC 相比 HTTP2 最大的性能優(yōu)勢(shì)。那什么是 0RTT 建連呢?
這里面有兩層含義:
- 傳輸層 0RTT 就能建立連接。
- 加密層 0RTT 就能建立加密連接。
上圖左邊是 HTTPS 的一次完全握手的建連過(guò)程,需要 2-3 個(gè) RTT才開始傳輸數(shù)據(jù),右邊 QUIC 協(xié)議在第一個(gè)包就可以包含有效的應(yīng)用數(shù)據(jù)
當(dāng)然,QUIC 協(xié)議可以實(shí)現(xiàn) 0RTT ,但這也是有條件的,實(shí)際上是非首次連接 1RTT,首次連接 0RTT,首次連接過(guò)程:
可以看到,首次連接的時(shí)候,在第 4 步時(shí),就已經(jīng)開始發(fā)送實(shí)際的業(yè)務(wù)數(shù)據(jù)了,而第 1 - 3 步正好一去一回花費(fèi)了 1RTT 時(shí)間,所以,首次連接的成本是 1RTT
3. 向前糾錯(cuò)機(jī)制
QUIC 協(xié)議有一個(gè)非常獨(dú)特的特性,稱為向前糾錯(cuò) (Forward Error Correction,F(xiàn)EC),每個(gè)數(shù)據(jù)包除了它本身的內(nèi)容之外,還包括了部分其他數(shù)據(jù)包的數(shù)據(jù),因此少量的丟包可以通過(guò)其他包的冗余數(shù)據(jù)直接組裝而無(wú)需重傳。
向前糾錯(cuò)犧牲了每個(gè)數(shù)據(jù)包可以發(fā)送數(shù)據(jù)的上限,但是減少了因?yàn)閬G包導(dǎo)致的數(shù)據(jù)重傳,因?yàn)閿?shù)據(jù)重傳將會(huì)消耗更多的時(shí)間(包括確認(rèn)數(shù)據(jù)包丟失、請(qǐng)求重傳、等待新數(shù)據(jù)包等步驟的時(shí)間消耗)。
假如說(shuō)這次我要發(fā)送三個(gè)包,那么協(xié)議會(huì)算出這三個(gè)包的異或值并單獨(dú)發(fā)出一個(gè)校驗(yàn)包,也就是總共發(fā)出了四個(gè)包。
當(dāng)出現(xiàn)其中的非校驗(yàn)包丟包的情況時(shí),可以通過(guò)另外三個(gè)包計(jì)算出丟失的數(shù)據(jù)包的內(nèi)容。
當(dāng)然這種技術(shù)只能使用在丟失一個(gè)包的情況下,如果出現(xiàn)丟失多個(gè)包就不能使用糾錯(cuò)機(jī)制了,只能使用重傳的方式了。
4. 加密認(rèn)證的報(bào)文
TCP 協(xié)議頭部沒(méi)有經(jīng)過(guò)任何加密和認(rèn)證,所以在傳輸過(guò)程中很容易被中間網(wǎng)絡(luò)設(shè)備篡改,注入和竊聽(tīng)。比如修改序列號(hào)、滑動(dòng)窗口。這些行為有可能是出于性能優(yōu)化,也有可能是主動(dòng)攻擊。
但是 QUIC 的 packet 可以說(shuō)是武裝到了牙齒。除了個(gè)別報(bào)文比如 PUBLIC_RESET 和 CHLO,所有報(bào)文頭部都是經(jīng)過(guò)認(rèn)證的,報(bào)文 Body 都是經(jīng)過(guò)加密的。
這樣只要對(duì) QUIC 報(bào)文任何修改,接收端都能夠及時(shí)發(fā)現(xiàn),有效地降低了安全風(fēng)險(xiǎn)。
如上圖所示,紅色部分是 Stream Frame 的報(bào)文頭部,有認(rèn)證。綠色部分是報(bào)文內(nèi)容,全部經(jīng)過(guò)加密。
QUIC 和 HTTP/3 前景發(fā)展展望
QUIC 協(xié)議雖然是基于 UDP 來(lái)實(shí)現(xiàn)的,但它將 TCP 的重要功能都進(jìn)行了實(shí)現(xiàn)和優(yōu)化,同時(shí)在加密傳輸方向的嘗試也推動(dòng)了TLS1.3的發(fā)展,未來(lái)還是可期的
只是現(xiàn)在 TCP 協(xié)議的勢(shì)力過(guò)于強(qiáng)大,很多網(wǎng)絡(luò)設(shè)備甚至對(duì)于UDP數(shù)據(jù)包做了很多不友好的策略,所以現(xiàn)在暫時(shí)還是 TCP 的天下???♀?,不過(guò) QUIC 已經(jīng)展現(xiàn)了強(qiáng)大的生命力,讓我們拭目以待吧!
參考
圖解|為什么HTTP3.0使用UDP協(xié)議:https://network.51cto.com/art/202009/625999.htm
如何看待 HTTP/3 ?:https://www.zhihu.com/question/302412059/answer/533223530
來(lái)自:https://github.com/Advanced-Frontend/Daily-Interview-Question