前言
HTTP/2 相比于 HTTP/1,可以說是大幅度提高了網頁的性能,隻需要更新到該協定就可以減少很多之前需要做的性能優化工作,當然相容問題以及如何優雅降級應該是國内還不普遍使用的原因之一。
雖然 HTTP/2 提高了網頁的性能,但是并不代表它已經是完美的了,HTTP/3 就是為了解決 HTTP/2 所存在的一些問題而被推出來的。
一、HTTP協定
HTTP協定是HyperText Transfer Protocol(超文本傳輸協定)的縮寫,它是網際網路上應用最為廣泛的一種網絡協定。所有的WWW檔案都必須遵守這個标準。伴随着計算機網絡和浏覽器的誕生,HTTP1.0也随之而來,處于計算機網絡中的應用層,HTTP是建立在TCP協定之上,是以HTTP協定的瓶頸及其優化技巧都是基于TCP協定本身的特性,例如tcp建立連接配接的3次握手和斷開連接配接的4次揮手以及每次建立連接配接帶來的RTT延遲時間。
二、HTTP/1.x的缺陷
- 連接配接無法複用:連接配接無法複用會導緻每次請求都經曆三次握手和慢啟動。三次握手在高延遲的場景下影響較明顯,慢啟動則對大量小檔案請求影響較大(沒有達到最大視窗請求就被終止)。
- HTTP/1.0傳輸資料時,每次都需要重建立立連接配接,增加延遲。
- HTTP/1.1雖然加入keep-alive可以複用一部分連接配接,但域名分片等情況下仍然需要建立多個connection,耗費資源,給伺服器帶來性能壓力。
- Head-Of-Line Blocking(HOLB):導緻帶寬無法被充分利用,以及後續健康請求被阻塞。 HOLB 是指一系列包(package)因為第一個包被阻塞;當頁面中需要請求很多資源的時候,HOLB(隊頭阻塞)會導緻在達到最大請求數量時,剩餘的資源需要等待其他資源請求完成後才能發起請求。
- HTTP 1.0:下個請求必須在前一個請求傳回後才能發出,
對按序發生。顯然,如果某個請求長時間沒有傳回,那麼接下來的請求就全部阻塞了。request-response
- HTTP 1.1:嘗試使用 pipeling 來解決,即浏覽器可以一次性發出多個請求(同個域名,同一條 TCP 連結)。但 pipeling 要求傳回是按序的,那麼前一個請求如果很耗時(比如處理大圖檔),那麼後面的請求即使伺服器已經處理完,仍會等待前面的請求處理完才開始按序傳回。是以,pipeling 隻部分解決了 HOLB。
- HTTP 1.0:下個請求必須在前一個請求傳回後才能發出,

如上圖所示,紅色圈出來的請求就因域名連結數已超過限制,而被挂起等待了一段時間。
- 協定開銷大: HTTP1.x在使用時,header裡攜帶的内容過大,在一定程度上增加了傳輸的成本,并且每次請求header基本不怎麼變化,尤其在移動端增加使用者流量。
- 安全因素:HTTP1.x在傳輸資料時,所有傳輸的内容都是明文,用戶端和伺服器端都無法驗證對方的身份,這在一定程度上無法保證資料的安全性
三、SPDY 協定
因為HTTP/1.x的問題,我們會引入雪碧圖、将小圖内聯、使用多個域名等等的方式來提高性能。不過這些優化都繞開了協定,直到2009年,谷歌公開了自行研發的 SPDY 協定,主要解決HTTP/1.1效率不高的問題。谷歌推出SPDY,才算是正式改造HTTP協定本身。降低延遲,壓縮header等等,SPDY的實踐證明了這些優化的效果,也最終帶來HTTP/2的誕生。
SPDY 協定在Chrome浏覽器上證明可行以後,就被當作 HTTP/2 的基礎,主要特性都在 HTTP/2 之中得到繼承。
四、HTTP/2 簡介
2015年,HTTP/2 釋出。HTTP/2是現行HTTP協定(HTTP/1.x)的替代,但它不是重寫,HTTP方法/狀态碼/語義都與HTTP/1.x一樣。HTTP/2基于SPDY3,專注于性能,最大的一個目标是在使用者和網站間隻用一個連接配接(connection)。
HTTP/2由兩個規範(Specification)組成:
- Hypertext Transfer Protocol version 2 - RFC7540
- HPACK - Header Compression for HTTP/2 - RFC7541
五、HTTP/2 新特性
1. 二進制傳輸
HTTP/2 采用二進制格式傳輸資料,而非 HTTP 1.x 的文本格式,二進制協定解析起來更高效。 HTTP / 1 的請求和響應封包,都是由起始行,首部和實體正文(可選)組成,各部分之間以文本換行符分隔。HTTP/2 将請求和響應資料分割為更小的幀,并且它們采用二進制編碼。
接下來我們介紹幾個重要的概念:
- 流:流是連接配接中的一個虛拟信道,可以承載雙向的消息;每個流都有一個唯一的整數辨別符(1、2…N);
- 消息:是指邏輯上的 HTTP 消息,比如請求、響應等,由一或多個幀組成。
- 幀:HTTP 2.0 通信的最小機關,每個幀包含幀首部,至少也會辨別出目前幀所屬的流,承載着特定類型的資料,如 HTTP 首部、負荷,等等
HTTP/2 中,同域名下所有通信都在單個連接配接上完成,該連接配接可以承載任意數量的雙向資料流。每個資料流都以消息的形式發送,而消息又由一個或多個幀組成。多個幀之間可以亂序發送,根據幀首部的流辨別可以重新組裝。
2. 多路複用
在 HTTP/2 中引入了多路複用的技術。多路複用很好的解決了浏覽器限制同一個域名下的請求數量的問題,同時也接更容易實作全速傳輸,畢竟新開一個 TCP 連接配接都需要慢慢提升傳輸速度。
大家可以通過
該連結直覺感受下 HTTP/2 比 HTTP/1 到底快了多少。
在 HTTP/2 中,有了二進制分幀之後,HTTP /2 不再依賴 TCP 連結去實作多流并行了,在 HTTP/2中:
- 同域名下所有通信都在單個連接配接上完成。
- 單個連接配接可以承載任意數量的雙向資料流。
- 資料流以消息的形式發送,而消息又由一個或多個幀組成,多個幀之間可以亂序發送,因為根據幀首部的流辨別可以重新組裝。
這一特性,使性能有了極大提升:
- 同個域名隻需要占用一個 TCP 連接配接,使用一個連接配接并行發送多個請求和響應,消除了因多個 TCP 連接配接而帶來的延時和記憶體消耗。
- 并行交錯地發送多個請求,請求之間互不影響。
- 并行交錯地發送多個響應,響應之間互不幹擾。
- 在HTTP/2中,每個請求都可以帶一個31bit的優先值,0表示最高優先級, 數值越大優先級越低。有了這個優先值,用戶端和伺服器就可以在處理不同的流時采取不同的政策,以最優的方式發送流、消息和幀。
如上圖所示,多路複用的技術可以隻通過一個 TCP 連接配接就可以傳輸所有的請求資料。
3. Header 壓縮
在 HTTP/1 中,我們使用文本的形式傳輸 header,在 header 攜帶 cookie 的情況下,可能每次都需要重複傳輸幾百到幾千的位元組。
為了減少這塊的資源消耗并提升性能, HTTP/2對這些首部采取了壓縮政策:
- HTTP/2在用戶端和伺服器端使用“首部表”來跟蹤和存儲之前發送的鍵-值對,對于相同的資料,不再通過每次請求和響應發送;
- 首部表在HTTP/2的連接配接存續期内始終存在,由用戶端和伺服器共同漸進地更新;
- 每個新的首部鍵-值對要麼被追加到目前表的末尾,要麼替換表中之前的值
例如下圖中的兩個請求, 請求一發送了所有的頭部字段,第二個請求則隻需要發送差異資料,這樣可以減少備援資料,降低開銷
4. Server Push
Server Push即服務端能通過push的方式将用戶端需要的内容預先推送過去,也叫“cache push”。
可以想象以下情況,某些資源用戶端是一定會請求的,這時就可以采取服務端 push 的技術,提前給用戶端推送必要的資源,這樣就可以相對減少一點延遲時間。當然在浏覽器相容的情況下你也可以使用 prefetch。
例如服務端可以主動把JS和CSS檔案推送給用戶端,而不需要用戶端解析HTML時再發送這些請求。
服務端可以主動推送,用戶端也有權利選擇是否接收。如果服務端推送的資源已經被浏覽器緩存過,浏覽器可以通過發送RST_STREAM幀來拒收。主動推送也遵守同源政策,換句話說,伺服器不能随便将第三方資源推送給用戶端,而必須是經過雙方确認才行。
六、HTTP/3 新特性
1. HTTP/3簡介
雖然 HTTP/2 解決了很多之前舊版本的問題,但是它還是存在一個巨大的問題,主要是底層支撐的 TCP 協定造成的。
上文提到 HTTP/2 使用了多路複用,一般來說同一域名下隻需要使用一個 TCP 連接配接。但當這個連接配接中出現了丢包的情況,那就會導緻 HTTP/2 的表現情況反倒不如 HTTP/1 了。
因為在出現丢包的情況下,整個 TCP 都要開始等待重傳,也就導緻了後面的所有資料都被阻塞了。但是對于 HTTP/1.1 來說,可以開啟多個 TCP 連接配接,出現這種情況反到隻會影響其中一個連接配接,剩餘的 TCP 連接配接還可以正常傳輸資料。
那麼可能就會有人考慮到去修改 TCP 協定,其實這已經是一件不可能完成的任務了。因為 TCP 存在的時間實在太長,已經充斥在各種裝置中,并且這個協定是由作業系統實作的,更新起來不大現實。
基于這個原因,Google 就更起爐竈搞了一個基于 UDP 協定的 QUIC 協定,并且使用在了 HTTP/3 上,HTTP/3 之前名為 HTTP-over-QUIC,從這個名字中我們也可以發現,HTTP/3 最大的改造就是使用了 QUIC。
QUIC 雖然基于 UDP,但是在原本的基礎上新增了很多功能,接下來我們重點介紹幾個QUIC新功能。
2. QUIC新功能
- 0-RTT
通過使用類似 TCP 快速打開的技術,緩存目前會話的上下文,在下次恢複會話的時候,隻需要将之前的緩存傳遞給服務端驗證通過就可以進行傳輸了。0RTT 建連可以說是 QUIC 相比 HTTP2 最大的性能優勢。那什麼是 0RTT 建連呢?
這裡面有兩層含義:
- 傳輸層 0RTT 就能建立連接配接。
- 加密層 0RTT 就能建立加密連接配接。
上圖左邊是 HTTPS 的一次完全握手的建連過程,需要 3 個 RTT。就算是會話複用也需要至少 2 個 RTT。
而 QUIC 呢?由于建立在 UDP 的基礎上,同時又實作了 0RTT 的安全握手,是以在大部分情況下,隻需要 0 個 RTT 就能實作資料發送,在實作前向加密的基礎上,并且 0RTT 的成功率相比 TLS 的會話記錄單要高很多。
- 多路複用
雖然 HTTP/2 支援了多路複用,但是 TCP 協定終究是沒有這個功能的。QUIC 原生就實作了這個功能,并且傳輸的單個資料流可以保證有序傳遞且不會影響其他的資料流,這樣的技術就解決了之前 TCP 存在的問題。
同HTTP2.0一樣,同一條 QUIC連接配接上可以建立多個stream,來發送多個HTTP請求,但是,QUIC是基于UDP的,一個連接配接上的多個stream之間沒有依賴。比如下圖中stream2丢了一個UDP包,不會影響後面跟着 Stream3 和 Stream4,不存在 TCP 隊頭阻塞。雖然stream2的那個包需要重新傳,但是stream3、stream4的包無需等待,就可以發給使用者。
另外QUIC 在移動端的表現也會比 TCP 好。因為 TCP 是基于 IP 和端口去識别連接配接的,這種方式在多變的移動端網絡環境下是很脆弱的。但是 QUIC 是通過 ID 的方式去識别一個連接配接,不管你網絡環境如何變化,隻要 ID 不變,就能迅速重連上。
- 加密認證的封包
TCP 協定頭部沒有經過任何加密和認證,是以在傳輸過程中很容易被中間網絡裝置篡改,注入和竊聽。比如修改序列号、滑動視窗。這些行為有可能是出于性能優化,也有可能是主動攻擊。
但是 QUIC 的 packet 可以說是武裝到了牙齒。除了個别封包比如 PUBLIC_RESET 和 CHLO,所有封包頭部都是經過認證的,封包 Body 都是經過加密的。
這樣隻要對 QUIC 封包任何修改,接收端都能夠及時發現,有效地降低了安全風險。
如上圖所示,紅色部分是 Stream Frame 的封包頭部,有認證。綠色部分是封包内容,全部經過加密。
- 向前糾錯機制
QUIC協定有一個非常獨特的特性,稱為向前糾錯 (Forward Error Correction,FEC),每個資料包除了它本身的内容之外,還包括了部分其他資料包的資料,是以少量的丢包可以通過其他包的備援資料直接組裝而無需重傳。向前糾錯犧牲了每個資料包可以發送資料的上限,但是減少了因為丢包導緻的資料重傳,因為資料重傳将會消耗更多的時間(包括确認資料包丢失、請求重傳、等待新資料包等步驟的時間消耗)
假如說這次我要發送三個包,那麼協定會算出這三個包的異或值并單獨發出一個校驗包,也就是總共發出了四個包。當出現其中的非校驗包丢包的情況時,可以通過另外三個包計算出丢失的資料包的内容。當然這種技術隻能使用在丢失一個包的情況下,如果出現丢失多個包就不能使用糾錯機制了,隻能使用重傳的方式了。
七、總結
- HTTP/1.x 有連接配接無法複用、隊頭阻塞、協定開銷大和安全因素等多個缺陷
- HTTP/2 通過多路複用、二進制流、Header 壓縮等等技術,極大地提高了性能,但是還是存在着問題的
- QUIC 基于 UDP 實作,是 HTTP/3 中的底層支撐協定,該協定基于 UDP,又取了 TCP 中的精華,實作了即快又可靠的協定
參考文章
- HTTP2講解
- HTTP 2.0 協定詳解
- 前端面試之道
- 一文讀懂 HTTP/2 特性
- 科普:QUIC協定原理分析
- HTTP2簡介和基于HTTP2的Web優化
- HTTPHTTP2.0,SPDYHTTPS你應該知道的一些事
摘要: 學習 HTTP/2 與 HTTP/3。
- 原文: 一文讀懂HTTP/2 及 HTTP/3特性
- 作者: 浪裡行舟
經授權轉載,版權歸原作者所有。