來自公眾號(hào):騰訊技術(shù)工程
作者:charryhuang,騰訊 CSIG 前端開發(fā)工程師
從 1989 年萬(wàn)維網(wǎng)(www)誕生讯屈,HTTP(HyperText Transfer Protocol)經(jīng)歷了眾多版本迭代崭篡,WebSocket 也在期間萌芽蕊唐。1991 年 HTTP0.9 被發(fā)明艳馒。1996 年出現(xiàn)了 HTTP1.0。2015 年 HTTP2 正式發(fā)布匆浙。2020 年 HTTP3 或能正式使用安寺。以下將會(huì)簡(jiǎn)單介紹。
HTTP1.1 與 HTTP2
HTTP1.1 的缺陷
高延遲 — 隊(duì)頭阻塞(Head-Of-Line Blocking)
無狀態(tài)特性 — 阻礙交互
明文傳輸 — 不安全性
不支持服務(wù)端推送
隊(duì)頭阻塞
隊(duì)頭阻塞是指當(dāng)順序發(fā)送的請(qǐng)求序列中的一個(gè)請(qǐng)求因?yàn)槟撤N原因被阻塞時(shí)首尼,在后面排隊(duì)的所有請(qǐng)求也一并被阻塞挑庶,會(huì)導(dǎo)致客戶端遲遲收不到數(shù)據(jù)言秸。
針對(duì)隊(duì)頭阻塞:
1.將同一頁(yè)面的資源分散到不同域名下,提升連接上限迎捺。雖然能公用一個(gè) TCP 管道举畸,但是在一個(gè)管道中同一時(shí)刻只能處理一個(gè)請(qǐng)求,在當(dāng)前的請(qǐng)求沒有結(jié)束之前凳枝,其他的請(qǐng)求只能處于阻塞狀態(tài)抄沮。
2.減少請(qǐng)求數(shù)量
3.內(nèi)聯(lián)一些資源:css、base64 圖片等
4.合并小文件減少資源數(shù)
無狀態(tài)特性
無狀態(tài)是指協(xié)議對(duì)于連接狀態(tài)沒有記憶能力岖瑰。純凈的 HTTP 是沒有 cookie 等機(jī)制的叛买,每一個(gè)連接都是一個(gè)新的連接。上一次請(qǐng)求驗(yàn)證了用戶名密碼蹋订,而下一次請(qǐng)求服務(wù)器并不知道它與上一條請(qǐng)求有何關(guān)聯(lián)率挣,換句話說就是掉登錄態(tài)。
不安全性
傳輸內(nèi)容沒有加密露戒,中途可能被篡改和劫持椒功。
SPDY 協(xié)議
SPDY 是由 google 推行的改進(jìn)版本的 HTTP1.1 (那時(shí)候還沒有 HTTP2)。
特性:
多路復(fù)用 — 解決隊(duì)頭阻塞
頭部壓縮 — 解決巨大的 HTTP 頭部
請(qǐng)求優(yōu)先級(jí) — 先獲取重要數(shù)據(jù)
服務(wù)端推送 — 填補(bǔ)空缺
提高安全性
多路復(fù)用
SPDY 允許在一個(gè)連接上無限制并發(fā)流智什。因?yàn)檎?qǐng)求在一個(gè)通道上动漾,TCP 效率更高(參考 TCP 擁塞控制 中的慢啟動(dòng))。更少的網(wǎng)絡(luò)連接荠锭,發(fā)出更密集的包旱眯。
頭部壓縮
使用專門的 HPACK 算法,每次請(qǐng)求和響應(yīng)只發(fā)送差異頭部节沦,一般可以達(dá)到 50% ~90% 的高壓縮率键思。
請(qǐng)求優(yōu)先級(jí)
雖然無限的并發(fā)流解決了隊(duì)頭阻塞的問題础爬,但如果帶寬受限甫贯,客戶端可能會(huì)因防止堵塞通道而阻止請(qǐng)求。在網(wǎng)絡(luò)通道被非關(guān)鍵資源堵塞時(shí)看蚜,高優(yōu)先級(jí)的請(qǐng)求會(huì)被優(yōu)先處理叫搁。
服務(wù)端推送
服務(wù)端推送(ServerPush),可以讓服務(wù)端主動(dòng)把資源文件推送給客戶端供炎。當(dāng)然客戶端也有權(quán)利選擇是否接收渴逻。
提高安全性
支持使用 HTTPS 進(jìn)行加密傳輸。
HTTP2
HTTP2 基于 SPDY音诫,專注于性能惨奕,最大的一個(gè)目標(biāo)是在用戶和網(wǎng)站間只用一個(gè)連接珠增。
新增特性:
二進(jìn)制分幀 - HTTP2 性能增強(qiáng)的核心
多路復(fù)用 - 解決串行的文件傳輸和連接數(shù)過多
二進(jìn)制分幀
首先鸦概,HTTP2 沒有改變 HTTP1 的語(yǔ)義古胆,只是在應(yīng)用層使用二進(jìn)制分幀方式傳輸渐北。因此,也引入了新的通信單位:幀卧波、消息时肿、流。
分幀有什么好處港粱?服務(wù)器單位時(shí)間接收到的請(qǐng)求數(shù)變多螃成,可以提高并發(fā)數(shù)。最重要的是查坪,為多路復(fù)用提供了底層支持寸宏。
多路復(fù)用
一個(gè)域名對(duì)應(yīng)一個(gè)連接,一個(gè)流代表了一個(gè)完整的請(qǐng)求-響應(yīng)過程偿曙。幀是最小的數(shù)據(jù)單位击吱,每個(gè)幀會(huì)標(biāo)識(shí)出該幀屬于哪個(gè)流,流也就是多個(gè)幀組成的數(shù)據(jù)流遥昧。多路復(fù)用覆醇,就是在一個(gè) TCP 連接中可以存在多個(gè)流。演示
HTTP2 的缺陷
TCP 以及 TCP+TLS 建立連接的延時(shí)
TCP 的隊(duì)頭阻塞并沒有徹底解決
多路復(fù)用導(dǎo)致服務(wù)器壓力上升
多路復(fù)用容易 Timeout
建連延時(shí)
TCP 連接需要和服務(wù)器進(jìn)行三次握手炭臭,即消耗完 1.5 個(gè) RTT 之后才能進(jìn)行數(shù)據(jù)傳輸永脓。
TLS 連接有兩個(gè)版本—— TLS1.2 和 TLS1.3,每個(gè)版本建立連接所花的時(shí)間不同鞋仍,大致需要 1~2 個(gè) RTT常摧。
RTT(Round-Trip Time):往返時(shí)延。表示從發(fā)送端發(fā)送數(shù)據(jù)開始威创,到發(fā)送端收到來自接收端的確認(rèn)(接收端收到數(shù)據(jù)后便立即發(fā)送確認(rèn))落午,總共經(jīng)歷的時(shí)延。
隊(duì)頭阻塞沒有徹底解決
TCP 為了保證可靠傳輸肚豺,有一個(gè)“超時(shí)重傳”機(jī)制溃斋,丟失的包必須等待重傳確認(rèn)。HTTP2 出現(xiàn)丟包時(shí)吸申,整個(gè) TCP 都要等待重傳梗劫,那么就會(huì)阻塞該 TCP 連接中的所有請(qǐng)求。
RTO:英文全稱是 Retransmission TimeOut截碴,即重傳超時(shí)時(shí)間梳侨;RTO 是一個(gè)動(dòng)態(tài)值,會(huì)根據(jù)網(wǎng)絡(luò)的改變而改變日丹。RTO 是根據(jù)給定連接的往返時(shí)間 RTT 計(jì)算出來的走哺。接收方返回的 ack 是希望收到的下一組包的序列號(hào)。
多路復(fù)用導(dǎo)致服務(wù)器壓力上升
多路復(fù)用沒有限制同時(shí)請(qǐng)求數(shù)哲虾。請(qǐng)求的平均數(shù)量與往常相同丙躏,但實(shí)際會(huì)有許多請(qǐng)求的短暫爆發(fā)齐帚,導(dǎo)致瞬時(shí) QPS 暴增。
多路復(fù)用容易 Timeout
大批量的請(qǐng)求同時(shí)發(fā)送彼哼,由于 HTTP2 連接內(nèi)存在多個(gè)并行的流对妄,而網(wǎng)絡(luò)帶寬和服務(wù)器資源有限,每個(gè)流的資源會(huì)被稀釋敢朱,雖然它們開始時(shí)間相差更短剪菱,但卻都可能超時(shí)。
即使是使用 Nginx 這樣的負(fù)載均衡器拴签,想正確進(jìn)行節(jié)流也可能很棘手孝常。其次,就算你向應(yīng)用程序引入或調(diào)整排隊(duì)機(jī)制蚓哩,但一次能處理的連接也是有限的构灸。如果對(duì)請(qǐng)求進(jìn)行排隊(duì),還要注意在響應(yīng)超時(shí)后丟棄請(qǐng)求岸梨,以避免浪費(fèi)不必要的資源喜颁。引用
QUIC
簡(jiǎn)介
Google
在推 SPDY 的時(shí)候就已經(jīng)意識(shí)到了這些問題,于是就另起爐灶搞了一個(gè)基于 UDP 協(xié)議的 QUIC 協(xié)議曹阔。而這個(gè)就是 HTTP3半开。它真正“完美”地解決了“隊(duì)頭阻塞”問題。
主要特點(diǎn)
改進(jìn)的擁塞控制赃份、可靠傳輸
快速握手
集成了 TLS 1.3 加密
多路復(fù)用
連接遷移
改進(jìn)的擁塞控制寂拆、可靠傳輸
從擁塞算法和可靠傳輸本身來看,QUIC 只是按照 TCP 協(xié)議重新實(shí)現(xiàn)了一遍抓韩,那么 QUIC 協(xié)議到底改進(jìn)在哪些方面呢纠永?主要有如下幾點(diǎn):
1. 可插拔 — 應(yīng)用程序?qū)用婢湍軐?shí)現(xiàn)不同的擁塞控制算法。
一個(gè)應(yīng)用程序的不同連接也能支持配置不同的擁塞控制谒拴。應(yīng)用程序不需要停機(jī)和升級(jí)就能實(shí)現(xiàn)擁塞控制的變更尝江,可以針對(duì)不同業(yè)務(wù),不同網(wǎng)絡(luò)制式彪薛,甚至不同的 RTT茂装,使用不同的擁塞控制算法怠蹂。
關(guān)于應(yīng)用層的可插拔擁塞控制模擬善延,可以對(duì) socket 上的流為對(duì)象進(jìn)行實(shí)驗(yàn)。
2. 單調(diào)遞增的 Packet Number — 使用 Packet Number 代替了 TCP 的 seq城侧。
每個(gè) Packet Number 都嚴(yán)格遞增易遣,也就是說就算 Packet N 丟失了,重傳的 Packet N 的 Packet Number 已經(jīng)不是 N嫌佑,而是一個(gè)比 N 大的值豆茫。而 TCP 重傳策略存在二義性侨歉,比如客戶端發(fā)送了一個(gè)請(qǐng)求,一個(gè) RTO 后發(fā)起重傳揩魂,而實(shí)際上服務(wù)器收到了第一次請(qǐng)求幽邓,并且響應(yīng)已經(jīng)在路上了,當(dāng)客戶端收到響應(yīng)后火脉,得出的 RTT 將會(huì)比真實(shí) RTT 要小牵舵。當(dāng) Packet N 唯一之后,就可以計(jì)算出正確的 RTT倦挂。
3. 不允許 Reneging — 一個(gè) Packet 只要被 Ack畸颅,就認(rèn)為它一定被正確接收。
Reneging 的意思是方援,接收方有權(quán)把已經(jīng)報(bào)給發(fā)送端 SACK(Selective Acknowledgment) 里的數(shù)據(jù)給丟了(如接收窗口不夠而丟棄亂序的包)没炒。
QUIC 中的 ACK 包含了與 TCP 中 SACK 等價(jià)的信息,但 QUIC 不允許任何(包括被確認(rèn)接受的)數(shù)據(jù)包被丟棄犯戏。這樣不僅可以簡(jiǎn)化發(fā)送端與接收端的實(shí)現(xiàn)難度送火,還可以減少發(fā)送端的內(nèi)存壓力。
4. 前向糾錯(cuò)(FEC)
早期的 QUIC 版本存在一個(gè)丟包恢復(fù)機(jī)制先匪,但后來由于增加帶寬消耗和效果一般而廢棄漾脂。FEC 中,QUIC 數(shù)據(jù)幀的數(shù)據(jù)混合原始數(shù)據(jù)和冗余數(shù)據(jù)胚鸯,來確保無論到達(dá)接收端的 n 次傳輸內(nèi)容是什么骨稿,接收端都能夠恢復(fù)所有 n 個(gè)原始數(shù)據(jù)包。FEC 的實(shí)質(zhì)就是異或姜钳。示意圖:
5. 更多的 Ack 塊和增加 Ack Delay 時(shí)間坦冠。
QUIC 可以同時(shí)提供 256 個(gè) Ack Block,因此在重排序時(shí)哥桥,QUIC 相對(duì)于 TCP(使用 SACK)更有彈性辙浑,這也使得在重排序或丟失出現(xiàn)時(shí),QUIC 可以在網(wǎng)絡(luò)上保留更多的在途字節(jié)拟糕。在丟包率比較高的網(wǎng)絡(luò)下判呕,可以提升網(wǎng)絡(luò)的恢復(fù)速度,減少重傳量送滞。
TCP 的 Timestamp 選項(xiàng)存在一個(gè)問題:發(fā)送方在發(fā)送報(bào)文時(shí)設(shè)置發(fā)送時(shí)間戳侠草,接收方在確認(rèn)該報(bào)文段時(shí)把時(shí)間戳字段值復(fù)制到確認(rèn)報(bào)文時(shí)間戳,但是沒有計(jì)算接收端接收到包到發(fā)送 Ack 的時(shí)間犁嗅。這個(gè)時(shí)間可以簡(jiǎn)稱為 Ack Delay边涕,會(huì)導(dǎo)致 RTT 計(jì)算誤差。現(xiàn)在就是把這個(gè)東西加進(jìn)去計(jì)算 RTT 了。
6. 基于 stream 和 connection 級(jí)別的流量控制功蜓。
為什么需要兩類流量控制呢园爷?主要是因?yàn)?QUIC 支持多路復(fù)用。Stream 可以認(rèn)為就是一條 HTTP 請(qǐng)求式撼。Connection 可以類比一條 TCP 連接童社。多路復(fù)用意味著在一條 Connetion 上會(huì)同時(shí)存在多條 Stream。
QUIC 接收者會(huì)通告每個(gè)流中最多想要接收到的數(shù)據(jù)的絕對(duì)字節(jié)偏移著隆。隨著數(shù)據(jù)在特定流中的發(fā)送叠洗,接收和傳送,接收者發(fā)送 WINDOW_UPDATE 幀旅东,該幀增加該流的通告偏移量限制灭抑,允許對(duì)端在該流上發(fā)送更多的數(shù)據(jù)。
除了每個(gè)流的流控制外抵代,QUIC 還實(shí)現(xiàn)連接級(jí)的流控制腾节,以限制 QUIC 接收者愿意為連接分配的總緩沖區(qū)。連接的流控制工作方式與流的流控制一樣荤牍,但傳送的字節(jié)和最大的接收偏移是所有流的總和案腺。
最重要的是,我們可以在內(nèi)存不足或者上游處理性能出現(xiàn)問題時(shí)康吵,通過流量控制來限制傳輸速率劈榨,保障服務(wù)可用性。
快速握手
由于 QUIC 是基于 UDP 的晦嵌,所以 QUIC 可以實(shí)現(xiàn) 0-RTT 或者 1-RTT 來建立連接同辣,可以大大提升首次打開頁(yè)面的速度。
集成了 TLS 1.3 加密
TLS 1.3 支持 3 種基本密鑰交換模式:
(EC)DHE (基于有限域或橢圓曲線的 Diffie-Hellman)
PSK - only
PSK with (EC)DHE
在完全握手情況下惭载,需要 1-RTT 建立連接旱函。TLS1.3 恢復(fù)會(huì)話可以直接發(fā)送加密后的應(yīng)用數(shù)據(jù),不需要額外的 TLS 握手描滔,也就是 0-RTT棒妨。
TLS 1.3 0-RTT 簡(jiǎn)單原理示意(基于 DHE):
但是 TLS1.3 也并不完美。TLS 1.3 的 0-RTT 無法保證前向安全性(Forward secrecy)含长。簡(jiǎn)單講就是券腔,如果當(dāng)攻擊者通過某種手段獲取到了 Session Ticket Key,那么該攻擊者可以解密以前的加密數(shù)據(jù)拘泞。
要緩解該問題可以通過設(shè)置使得與 Session Ticket Key 相關(guān)的 DH 靜態(tài)參數(shù)在短時(shí)間內(nèi)過期(一般幾個(gè)小時(shí))纷纫。
多路復(fù)用
QUIC 是為多路復(fù)用從頭設(shè)計(jì)的,攜帶個(gè)別流的的數(shù)據(jù)的包丟失時(shí)田弥,通常只影響該流涛酗。QUIC 連接上的多個(gè) stream 之間并沒有依賴,也不會(huì)有底層協(xié)議限制偷厦。假如 stream2 丟了一個(gè)包商叹,也只會(huì)影響 stream2 的處理。
連接遷移
TCP 是按照 4 要素(客戶端 IP只泼、端口, 服務(wù)器 IP剖笙、端口)確定一個(gè)連接的。而 QUIC 則是讓客戶端生成一個(gè) Connection ID (64 位)來區(qū)別不同連接请唱。只要 Connection ID 不變弥咪,連接就不需要重新建立,即便是客戶端的網(wǎng)絡(luò)發(fā)生變化十绑。由于遷移客戶端繼續(xù)使用相同的會(huì)話密鑰來加密和解密數(shù)據(jù)包聚至,QUIC 還提供了遷移客戶端的自動(dòng)加密驗(yàn)證。
挑戰(zhàn)
NAT 問題
NAT 概念
為了解決 IP 地址不足的問題本橙,NAT 給一個(gè)局域網(wǎng)絡(luò)只分配一個(gè) IP 地址扳躬,這個(gè)網(wǎng)絡(luò)內(nèi)的主機(jī),則分配私有地址甚亭,這些私有地址對(duì)外是不可見的贷币,他們對(duì)外的通信都要借助那個(gè)唯一分配的 IP 地址。所有離開本地網(wǎng)絡(luò)去往 Internet 的數(shù)據(jù)報(bào)的源 IP 地址需替換為相同的 NAT亏狰,區(qū)別僅在于端口號(hào)不同役纹。
原因
TCP 和 UDP 的報(bào)文頭部不同導(dǎo)致 NAT 問題的出現(xiàn)。
NAT 設(shè)備的端口記憶問題
對(duì)于基于 TCP 的 HTTP暇唾、HTTPS 傳輸促脉,NAT 設(shè)備可以根據(jù) TCP 報(bào)文頭的 SYN/FIN 狀態(tài)位,知道通信什么時(shí)候開始策州,什么時(shí)候結(jié)束嘲叔,對(duì)應(yīng)記憶 NAT 映射的開始和結(jié)束。
但是基于 UDP 傳輸?shù)?HTTP3 抽活,不存在 SYN/FIN 狀態(tài)位硫戈。NAT 設(shè)備的記憶如果短于用戶會(huì)話時(shí)間,則用戶會(huì)話會(huì)中斷下硕。NAT 設(shè)備的記憶時(shí)間如果長(zhǎng)于用戶會(huì)話時(shí)間丁逝,則意味著 NAT 設(shè)備的端口資源會(huì)被白白占用。
最直接的解決方案是梭姓,在 QUIC 的頭部模仿 TCP 的 SYN/FIN 狀態(tài)霜幼,讓沿途的 NAT 設(shè)備知道會(huì)話什么時(shí)候開始、什么時(shí)候結(jié)束誉尖。但這需要升級(jí)全球所有的 NAT 設(shè)備的軟件罪既。
另外一個(gè)可行的方案是,讓 QUIC 周期性地發(fā)送 Keepalive 消息,刷新 NAT 設(shè)備的記憶琢感,避免 NAT 設(shè)備自動(dòng)釋放丢间。
NAT 設(shè)備禁用 UDP
在一些 NAT 網(wǎng)絡(luò)環(huán)境下(如某些校園網(wǎng)),UDP 協(xié)議會(huì)被路由器等中間網(wǎng)絡(luò)設(shè)備禁止驹针,這時(shí)客戶端會(huì)直接降級(jí)烘挫,選擇 HTTPS 等備選通道,保證正常業(yè)務(wù)請(qǐng)求柬甥。
NGINX 負(fù)載均衡問題概念
QUIC 客戶端存在網(wǎng)絡(luò)制式切換饮六,就算是同一個(gè)移動(dòng)機(jī)房,可能第一次業(yè)務(wù)請(qǐng)求時(shí)會(huì)落到 A 這臺(tái)服務(wù)器苛蒲,后續(xù)再次連接卤橄,就會(huì)落到 B 實(shí)例上,重復(fù)走 1-RTT 的完整握手流程臂外。
全局握手緩存
為所有 QUIC 服務(wù)器實(shí)例建立一個(gè)全局握手緩存窟扑。當(dāng)用戶網(wǎng)絡(luò)發(fā)生切換時(shí),下一次的業(yè)務(wù)請(qǐng)求無論是落到哪一個(gè)機(jī)房或哪一臺(tái)實(shí)例上寄月,握手建連都會(huì)是 0-RTT辜膝。
歷代 HTTP 速度測(cè)試
結(jié)尾
從古至今實(shí)時(shí)數(shù)據(jù)傳輸(音頻、視頻漾肮、游戲等)都面臨卡頓厂抖、延遲等問題,而 QUIC 基于 UDP 的架構(gòu)和改進(jìn)的重傳等特性克懊,能夠有效的提升用戶體驗(yàn)忱辅。目前
B 站 也已經(jīng)接入 QUIC。
如果想要自己體驗(yàn) QUIC谭溉,可以使用 Libquic墙懂、Caddy 等。另外 github 上面也有 C++版本的 QUIC 實(shí)現(xiàn)扮念,利用 Nodejs 的 C++ 模塊损搬,前端工程師也可以快速實(shí)現(xiàn)一個(gè) node-quic。
參考資料
1柜与、http2.0 原理詳細(xì)分析
https://www.huaijiujia.com/2018/06/30/http協(xié)議-http2-0原理詳細(xì)分析/
2巧勤、HPACK: HTTP/2 里的沉默殺手
https://www.zcfy.cc/article/hpack-the-silent-killer-feature-of-http-2-1969.html
3、QPACK:HTTP /3 的頭壓縮
https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html
4弄匕、DH 算法
https://zh.wikipedia.org/wiki/迪菲-赫爾曼密鑰交換
5颅悉、前向安全(ForwardSecrecy)
https://zh.wikipedia.org/wiki/前向保密
6、TLS 1.3 VS TLS 1.2迁匠,讓你明白 TLS 1.3 的強(qiáng)大
http://www.reibang.com/p/efe44d4a7501
7剩瓶、CaddyWeb 服務(wù)器 QUIC 部署
https://www.wolfcstech.com/2017/01/09/Caddy%20Web服務(wù)器QUIC部署/
8驹溃、關(guān)于 QUIC 的各種嘗試
https://debug.fanzheng.org/post/about-quic.html#toc-3b3
9、使用 QUIC 協(xié)議實(shí)現(xiàn)實(shí)時(shí)視頻直播 0 卡頓
https://zhuanlan.zhihu.com/p/33902646
10延曙、解密 HTTP/2 與 HTTP/3 的新特性
https://www.infoq.cn/article/kU4OkqR8vH123a8dLCCJ
11豌鹤、Web通信協(xié)議,你還需要知道:SPDY 和 QUIC
https://segmentfault.com/a/1190000016265991
12搂鲫、如何看待 HTTP/3 傍药?