HTTP/1.0 就像是“一問一答”的電話,每次打電話(請求)都得先撥號(建立連接),說完一句話(發送數據)就掛斷(關閉連接),再打下一通電話。效率比較低。
HTTP/2.0 就像是“多路復用”的電話會議,一次撥號(建立連接)后,大家可以在同一個會議室里同時說多句話(多路復用),而且還可以壓縮語言(頭部壓縮),甚至會議主持人(服務器)可以提前把大家可能需要的文件準備好(服務器推送)。這樣效率就高多了。
為什么需要 HTTP?
在理解 HTTP/1.0 和 HTTP/2.0 的區別之前,我們得先明白 HTTP 協議本身是干嘛的。
想象一下,互聯網就像一個巨大的圖書館。你(客戶端)想從圖書館里借一本書(資源),圖書館管理員(服務器)需要知道你要哪本書,然后把書給你。HTTP(HyperText Transfer Protocol,超文本傳輸協議)就是你和圖書館管理員之間交流的“語言”或“規矩”。它規定了你如何提出請求,管理員如何回應,以及數據如何傳輸。
核心目標: 讓客戶端和服務器能夠高效、可靠地交換信息(主要是網頁、圖片、視頻等資源)。
從 1.0 到 2.0
HTTP/1.0:初期的“一問一答”模式
HTTP/1.0 是互聯網早期設計的協議,它非常簡單直接,就像我們上面說的“一問一答”的電話。
核心思想: 每次請求-響應都建立一個新的 TCP 連接,完成后立即關閉。
- 用戶需求: 我要一個網頁。
- 網頁構成: 一個網頁通常不只包含 HTML 文本,還有圖片、CSS 文件、JavaScript 文件等等。
- 1.0 的做法:
- 客戶端請求 HTML 文件。
- 服務器響應 HTML 文件。
- 連接關閉。
- 客戶端解析 HTML,發現還需要圖片 A。
- 客戶端再次建立 TCP 連接,請求圖片 A。
- 服務器響應圖片 A。
- 連接關閉。
- …以此類推,直到所有資源都加載完畢。
這種模式有什么缺點?
- 連接建立/關閉開銷大: 每次建立 TCP 連接都需要“三次握手”,關閉需要“四次揮手”,這就像每次打電話都要先撥號、等待接通、再掛斷,非常耗時。
- 隊頭阻塞(Head-of-Line Blocking): 即使你有很多請求要發,也必須等前一個請求的響應完全回來,才能發送下一個請求。這就像你在排隊買票,前面的人沒買完,你就不能買。
- 帶寬利用率低: 連接頻繁建立和關閉,導致網絡帶寬無法持續高效利用。
流程圖:HTTP/1.0 請求流程
HTTP/1.1:小修小補,引入持久連接
HTTP/1.1 在 1.0 的基礎上做了一些改進,最核心的就是引入了持久連接(Persistent Connections),也叫 Keep-Alive。
默認情況下,一個 TCP 連接在發送完一個請求-響應后不會立即關閉,而是保持一段時間,允許在這個連接上發送后續的請求。
- 1.0 的痛點: 頻繁建立/關閉連接。
- 如何優化? 既然一個網頁需要多個資源,那能不能只建立一次連接,然后在這個連接上把所有資源都請求完再關閉呢?
- 1.1 的做法:
- 客戶端請求 HTML 文件。
- 建立 TCP 連接。
- 發送 HTTP 請求 (HTML)。
- 服務器響應 HTML。
- 連接保持開放。
- 客戶端解析 HTML,發現需要圖片 A。
- 在同一個 TCP 連接上,發送 HTTP 請求 (圖片 A)。
- 服務器響應圖片 A。
- 連接保持開放。
- …直到所有資源都加載完畢,或者達到超時時間,連接才關閉。
1.1 解決了連接建立/關閉的開銷,但還有什么問題?
- 隊頭阻塞依然存在: 雖然連接是持久的,但請求和響應仍然是串行的。你必須等前一個請求的響應完全回來,才能發送下一個請求。這就像你和朋友打電話,雖然沒掛斷,但你們還是得輪流說話,不能同時說。
- 頭部冗余: 每次請求都會發送大量的重復頭部信息(如 User-Agent, Accept 等),浪費帶寬。
- 沒有服務器推送: 服務器只能被動響應,不能主動推送客戶端可能需要的資源。
流程圖:HTTP/1.1 請求流程 (持久連接)
HTTP/2.0:徹底的性能革命
隨著網頁越來越復雜,資源越來越多,HTTP/1.1 的隊頭阻塞問題變得越來越突出。人們開始思考,有沒有一種方式,能讓一個連接同時處理多個請求和響應,就像多車道高速公路一樣?這就是 HTTP/2.0 的核心思想。
HTTP/2.0 基于 Google 的 SPDY 協議,它在應用層和傳輸層之間增加了一個二進制分幀層。
-
多路復用(Multiplexing): 在一個 TCP 連接上,同時發送多個請求和接收多個響應,且請求和響應之間互不影響。
-
二進制分幀(Binary Framing): 所有通信都被分解為更小的、獨立的幀,并以二進制格式傳輸。
-
頭部壓縮(Header Compression): 使用 HPACK 算法壓縮 HTTP 頭部,減少冗余數據傳輸。
-
服務器推送(Server Push): 服務器可以在客戶端請求某個資源時,主動推送客戶端可能需要的其他資源。
-
請求優先級(Request Prioritization): 客戶端可以為請求設置優先級,服務器可以根據優先級決定響應順序。
-
1.1 的痛點: 隊頭阻塞,頭部冗余,無服務器推送。
-
如何解決隊頭阻塞?
- 多路復用: 把每個請求和響應都拆分成小塊(幀),給每個幀一個唯一的標識符。然后這些幀可以在同一個 TCP 連接上亂序發送,接收方根據標識符再重新組裝。這就像快遞公司,把你的包裹拆成小件,然后和別人的小件一起裝車,到了目的地再根據單號重新組裝。
- 二進制分幀: 為什么是二進制?因為二進制解析效率高,更緊湊,不像文本協議那樣需要復雜的解析。
-
如何解決頭部冗余?
- 頭部壓縮: 很多請求的頭部信息是重復的,比如 User-Agent。我們可以維護一個“字典”(索引表),把常用的頭部信息存起來,下次只發送字典的索引號就行了。
-
如何提高加載速度?
- 服務器推送: 客戶端請求 HTML 頁面時,服務器知道這個頁面肯定需要 CSS 和 JS 文件,那服務器就可以在客戶端還沒請求 CSS 和 JS 之前,就把它們“推”給客戶端。這樣客戶端就不用再發請求了,節省了往返時間。
-
如何優化資源加載順序?
- 請求優先級: 客戶端可以告訴服務器,哪個資源更重要(比如 CSS 比圖片更重要),服務器就可以優先處理重要的請求。
流程圖:HTTP/2.0 請求流程
核心本質
- HTTP/1.0: 簡單粗暴,每次任務獨立完成。它的本質是串行處理,資源利用率低。
- HTTP/1.1: 在 1.0 基礎上打了個補丁,通過持久連接減少了連接開銷,但本質上還是串行請求-響應,只是在同一個管道里串行。
- HTTP/2.0: 徹底改變了傳輸方式,引入了多路復用。它的本質是并行處理,通過在應用層和傳輸層之間增加一個“調度層”(二進制分幀層),將邏輯上的多個流映射到物理上的一個 TCP 連接,從而解決了隊頭阻塞,并引入了更多優化手段。它不再是簡單的“一問一答”,而是更像一個智能的“數據管道”。
繼續思考
- 為什么不直接在 TCP 層解決隊頭阻塞? TCP 層的隊頭阻塞是數據包丟失重傳導致的。如果一個 TCP 包丟失了,即使后面的包都收到了,TCP 也必須等待丟失的包重傳成功并按序組裝,才能把數據交給應用層。HTTP/2.0 的多路復用是在應用層實現的,它解決了 HTTP 層面(邏輯流)的隊頭阻塞,但無法解決 TCP 層面(物理包)的隊頭阻塞。這也是為什么 HTTP/3.0 轉向 UDP (QUIC) 的原因之一,因為它想從傳輸層徹底解決隊頭阻塞。
- HTTP/2.0 的安全性: 雖然 HTTP/2.0 協議本身不強制加密,但幾乎所有主流瀏覽器都只支持基于 TLS/SSL 的 HTTP/2.0 (即 HTTPS)。這使得 HTTP/2.0 在實踐中比 HTTP/1.x 更安全。
- HTTP/2.0 的適用場景: 對于需要加載大量小文件、或者需要頻繁與服務器交互的單頁應用 (SPA) 等場景,HTTP/2.0 的性能優勢尤為明顯。
隊頭阻塞的本質是:在需要保證順序性的系統中,如果“隊頭”的元素處理受阻,那么“隊尾”的元素即使已經準備好,也無法越過隊頭被處理。
TCP 隊頭阻塞是為了保證數據包的可靠性和按序交付。如果一個數據包丟失,后續的數據包即使到達,也無法被應用層消費,因為 TCP 無法確定后續數據包的完整上下文。
HTTP/1.x 隊頭阻塞是因為 HTTP/1.x 協議設計上,在單個 TCP 連接中,請求和響應是嚴格串行且一一對應的。它沒有機制來區分和并行處理不同的邏輯流。
流程圖:HTTP/2.0 多路復用與流
為什么 HTTP/2.0 能解決 HTTP 層的隊頭阻塞?
HTTP/2.0 引入了多路復用。它把每個 HTTP 請求和響應都看作一個獨立的“流”(Stream),每個流都有自己的 ID。這些流的數據被拆分成更小的“幀”,這些幀可以在同一個 TCP 連接上亂序發送。接收方根據幀的 ID 重新組裝成完整的流。HTTP/2.0 解決了應用層(HTTP 協議層面)的隊頭阻塞,因為它不再強制請求和響應的串行順序。
HTTP/2.0 為什么不能解決 TCP 層的隊頭阻塞?
HTTP/2.0 仍然是基于 TCP 協議的。如果底層的 TCP 連接中,某個數據包丟失了,那么整個 TCP 連接仍然會因為等待這個丟失的數據包重傳而暫停,這會影響到所有在當前 TCP 連接上跑的 HTTP/2.0 流。這就像電話會議(HTTP/2.0)開得很好,但如果電話線(TCP 連接)斷了,所有人都受影響。
HTTP/3.0 如何解決 TCP 層的隊頭阻塞?
HTTP/3.0 放棄了 TCP,轉而使用基于 UDP 的 QUIC 協議。QUIC 協議在傳輸層實現了自己的可靠傳輸和多路復用機制。它為每個邏輯流分配獨立的序列號,這樣即使一個流的數據包丟失,也只會影響到這一個流,而不會阻塞其他流的數據傳輸。這就像,你和朋友們在不同的房間里打電話,即使一個房間的電話線斷了,其他房間的通話也不受影響。
特性 | HTTP/1.0 | HTTP/2.0 |
---|---|---|
連接管理 | 短連接:每個請求-響應建立新 TCP 連接,完成后立即關閉。 | 長連接/多路復用:一個 TCP 連接可同時處理多個請求和響應。 |
請求方式 | 串行請求:必須等待前一個響應完成后才能發送下一個請求。 | 并行請求:多個請求可同時發送,響應可亂序返回。 |
隊頭阻塞 | 存在:HTTP 層面和 TCP 層面都存在。 | 解決 HTTP 層面:通過多路復用解決。TCP 層面仍可能存在。 |
數據傳輸 | 文本協議:基于文本,解析相對復雜。 | 二進制分幀:所有數據分解為二進制幀傳輸,解析高效。 |
頭部處理 | 無壓縮:每次請求發送完整且重復的頭部信息。 | 頭部壓縮 (HPACK):壓縮頭部,減少冗余數據。 |
服務器推送 | 不支持:服務器只能被動響應。 | 支持:服務器可主動推送客戶端可能需要的資源。 |
請求優先級 | 不支持 | 支持:客戶端可設置請求優先級。 |
安全性 | 通常不加密 (HTTP) | 多數實現強制加密 (HTTPS) |
性能 | 較低,尤其在加載大量資源時。 | 顯著提高,尤其在加載大量資源時。 |
HTTP/2.0 是對 HTTP/1.x 的一次徹底的性能優化 對于HTTP/1.x,在服務器處理某個請求資源回復慢時,會阻塞其他請求,而2.0使用流的邏輯標識,可以同時發不同的請求,盡管帶寬是一樣的,卻可以降低延遲,更高效率的利用網絡傳輸通道,它將TCP的流組合成不同的請求數據,解決了應用層的隊頭阻塞,可是依舊是基于TCP流的,這意味,由于網絡層面的TCP出錯依舊會隊頭阻塞,并沒有解決本質問題
而3.0徹底解決了這個問題,3.0不再使用TCP而是UDP,每個請求都是額外的流,QUIC協議可以將傳輸和加密的握手結合在一起,如果客戶端之前連接過服務器,并且服務器支持,甚至可以實現 0-RTT 握手,即客戶端在發送第一個數據包時就包含應用數據,幾乎沒有延遲。 TCP 連接是基于 IP 地址和端口號的。如果你的設備從 Wi-Fi 切換到移動數據(IP 地址變化),TCP 連接就會斷開,需要重新建立。 QUIC 連接是基于一個 64 位的連接 ID。即使客戶端的 IP 地址或端口號發生變化,只要連接 ID 不變,QUIC 連接就可以保持活躍,無需重新建立。
流程圖:HTTP/3.0 (QUIC) 解決隊頭阻塞
UDP本身并不會有失敗重發的機制,所以即使是一個UDP連接網絡抖動不會影響整個UDP連接的數據重發,而是在QUIC協議負責每個流的重發,QUIC實現都是在應用層的,也因此對操作系統內核沒有額外要求,不需要更改,把TCP的可靠性實現由操作系統內核上升到應用層去實現這些