瀏覽器中的網絡:30 | HTTP/2:如何提升網絡速度?


前言:該篇說明:請見 說明 —— 瀏覽器工作原理與實踐 目錄

 

   上一篇文章聊了 HTTP/1.1 的發展史,雖然 HTTP/1.1 已經做了大量的優化,但是依然存在很多性能瓶頸,依然不能滿足我們日益變化的新需求,所以就有了今天要聊的 HTTP/2。

  本文依然從需求的層面來談,先分析 HTTP/1.1 存在哪些問題,然后再來分析 HTTP/2 是如何解決這些問題的。

  我們知道 HTTP/1.1 為網絡效率做了大量的優化,最核心的有如下三種方式:

  1. 增加了持久連接;
  2. 瀏覽器為每個域名最多同時維護 6 個 TPC 持久連接;
  3. 使用 CDN 的實現域名分片機制。 注:若不理解 域名分片機制,可以參考下圖。

  注: 使用cdn 的實現域名分片機制,文中有學員標注【將一個頁面的資源利用多個域名下載,提高 tcp 並發數量。http2 中已不需要】。標注在這做為一個理解參考。

  通過這些方式就大大提高了頁面的下載速度,你可以通過下圖來直觀感受下:

 

 

 HTTP/1.1 的資源下載方式

  在該圖中,引入了 CDN,並同時為每個域名維護 6 個連接,這樣就大大減輕了整個資源的下載時間。這里可以簡單計算下:如果使用單個 TCP 的持久連接,下載 100 個資源所花費的時間為 100 * n * RTT;若通過上面的技術,就可以把整個時間縮短為 100 * n * RTT/(6 * CDN 個數)。從這個計算結果來看,頁面加載速度變快了不少。

 

HTTP/1.1 的主要問題

  雖然 HTTP/1.1 采取了很多優化資源加載速度的策略,也取得了一定的效果,但是 HTTP/1.1對帶寬的利用率卻並不理想,這也是 HTTP/1.1 的一個核心問題。

  

  帶寬是指每秒最大能發送或者接收的字節數。我們把每秒能發送的最大字節數稱為上行帶寬,每秒能夠接收的最大字節數稱為下行帶寬

  之所以說 HTTP/1.1 對帶寬的利用率不理想,是因為 HTTP/1.1 很難將帶寬用滿。比如我們常說的 100M 帶寬,實際的下載速度能達到 12.5M/S,而采用 HTTP/1.1 時,也許在加載頁面資源時最大只能使用到 2.5M/S,很難將 12.5M 全部用滿。之所以會出現這個問題,主要是由以下三個原因導致的。

 

第一個原因,TCP 的慢啟動。

  注:由於 TCP 協議向應用層提供不定長的字節流發送方式,使得 TCP 協議先天性就有意願占滿整個帶寬,當許多 TCP 同時去占滿就有可能出現惡性擁塞事件。

  為了防止網絡的擁塞現象,TCP 提出了一系列的擁塞控制機制。最初由 V. Jacobson 在1988年的論文中提出的 TCP 的擁塞控制由“慢啟動(Slow start)”和“擁塞避免(Congestion avoidance)”組成,后來 TCP Reno 版本中又針對性的加入了“快速重傳(Fast retransmit)”、“快速恢復(Fast Recovery)”算法,再后來在 TCP NewReno 中又對“快速恢復”算法進行了改進,近些年又出現了選擇性應答(selective acknowledgement,SACK)算法,還有其他方面的大大小小的改進,成為網絡研究的一個熱點。 慢啟動是 TCP 與其他算法結合使用的擁塞控制策略的一部分,以避免發送超過網絡承載能力的數據,避免造成網絡擁塞。

  一旦一個 TCP 連接建立后,就進入了發送數據狀態,剛開始 TCP 協議會采用一個非常慢的速度去發送數據,然后慢慢加快發送數據的速度,直到發送數據的速度達到一個理想狀態,我們把這個過程成為慢啟動。

  你可以把每個 TCP 發送數據的過程看成是一輛車的啟動過程,當剛進入公路時,會有從 0 到一個穩定速度的提速過程,TCP 的慢啟動就類似於該過程。

  慢啟動是 TCP 為了減少網絡擁塞的一種策略,我們是沒有辦法改變的。

  而之所以說慢啟動會帶來性能問題,是因為頁面中常用的一些關鍵資源文件本來就不大,如 HTML 文件、CSS 文件和 JS文件,通常這些文件在 TCP 連接建立好之后就要發起請求的,但這個過程是慢啟動,所以耗費的時間比正常的時間要多很多,這樣就推遲了寶貴的首次渲染頁面的時長了。

 

第二個原因,同時開啟了多條 TCP 連接,那么這些連接會競爭固定的帶寬。

  你可以想象下,系統同時建立了多條 TCP 連接,當帶寬充足時,每條連接發送或接受速度會慢慢向上增加;而一旦帶寬不足時,這些 TCP 連接又會減慢發送或接收的速度。比如一個頁面有 200 個文件,使用了 3 個 CDN,那么加載該網頁的時候就需要建立 6*3,也就是 18 個 TCP連接來下載資源;在下載過程中,當發現帶寬不足的時候,各個 TCP 連接就需要動態減慢接收數據的速度。

  這樣就會出現一個問題,因為有的 TCP 連接下載的是一些關鍵資源,如 CSS 文件、JS 文件等,而有的 TCP 連接下載的是圖片、視頻等普通的資源文件,但多條 TCP 連接之間又不能協商讓哪些關鍵資源優先下載,這樣就有可能影響那些關鍵資源的下載速度了。  ——資源競爭時無法區分優先級。

  

第三個原因,HTTP/1.1 對頭阻塞的問題。

  通過上一篇文章知道在 HTTP/1.1 中使用持久連接時,雖然能公用一個 TCP 管道,但是在一個管道中同一時刻只能處理一個請求,在當前的請求沒有結束之前,其他的請求只能處於阻塞狀態。這意味着不能隨意在一個管道中發送請求和接收內容。

  這是一個很嚴重的問題,因為阻塞請求的因素有很多,並且都是一些不確定性的因素,假如有的請求被阻塞了 5 秒,那么后續排隊的請求都要延遲等待 5 秒,在這個等待的過程中,帶寬、CPU 都被白白浪費了。

  在瀏覽器處理生成頁面的過程中,是非常希望能提前接收到數據的,這樣就可以對這些數據做預處理操作,比如提前接收到了圖片,那么就可以提前進行編解碼操作,等到需要使用該圖片的時候,就可以直接給出處理后的數據了,這樣能讓用戶感受到整體速度的提升。

  但隊頭阻塞使得這些數據不能並行請求,所以隊頭阻塞是很不利於瀏覽器優化的。

 

HTTP/2 的多路復用

  前面分析了 HTTP/1.1 所存在的一些主要問題:慢啟動和 TCP 連接之間相互競爭帶寬是由於 TCP 本身的機制導致的,而隊頭阻塞是由於 HTTP/1.1 的機制導致的。

  那么該如何去解決這些問題呢?

 

  雖然 TCP 有問題,但是我們依然沒有換掉 TCP 的能力,所以就要想辦法去規避 TCP 的慢啟動和 TCP 連接之間的競爭問題。

  基於此,HTTP/2 的思路就是一個域名只使用一個 TCP 長連接來傳輸數據,這樣整個頁面資源的下載過程只需要一次慢啟動,同時也避免了多個 TCP 連接競爭帶寬所帶來的問題。另外,就是隊頭阻塞的問題,等待請求完成后才能去請求下一個資源,這種方式無疑是最慢的,所以 HTTP/2 需要實現資源的並行請求,也就是任何時候都可以將請求發送給服務器,而並不需要等待其他請求的完成,然后服務器也可以隨時返回處理好的請求資源給瀏覽器。所以,HTTP/2 的解決方案可以總結為:一個域名只使用一個 TCP 長連接和消除隊頭阻塞問題。可以參考下圖:

 

 

 HTTP/2 的多路復用

  該圖就是 HTTP/2 最核心、最重要且最具顛覆性的多路復用機制。從圖中你會發現每個請求都有一個對應的 ID,如 stream1 表示 index.html 的請求,stream2 表示 foo.css 的請求。這樣在瀏覽器端,就可以隨時將請求發送給服務器了。

  服務器端接收到這些請求后,會根據自己的喜好來決定優先返回哪些內容,比如服務器可能早就緩存好了 index.html 和 bar.js 的響應頭信息,那么當接收到請求的時候就可以立即把 index.html 和 bar.js 的響應頭信息返回給瀏覽器,然后再將 index.html 和 bar.js 的響應體數據返回給瀏覽器。之所以可以隨意發送,是因為每份數據都有對應的 ID,瀏覽器接收到之后,會篩選出相同 ID 的內容,將其拼接為完整的 HTTP 響應數據。

  HTTP/2 使用了多路復用技術,可以將請求分成一幀一幀的數據去傳輸,這樣帶來了一個額外的好處,就是當收到一個優先級高的請求時,比如接收到 JavaScript 或者 CSS 關鍵資源的請求,服務器可以暫停之前的請求來優先處理關鍵資源的請求。

 

多路復用的實現

  現在知道為了解決 HTTP/1.1 存在的問題,HTTP/2 采用了多路復用機制,那 HTTP/2 是怎么實現多路復用的呢?先看下面這張圖:

 

 HTTP/2 協議棧

  從圖中可以看出,HTTP/2 添加了一個二進制分幀層,那就結合圖來分析下 HTTP/2 的請求和接收過程。

  注:通過分幀層對相同請求的幀進行編號

  • 首先,瀏覽器准備好請求數據,包括了請求行、請求頭等信息,如果是 POST 方法,那么還要有請求體。
  • 這些數據經過二進制分幀層處理之后,會被轉換為一個個帶有請求 ID 編號的幀,通過協議棧將這些幀發送給服務器。
  • 服務器接收到所有幀之后,會將所有相同 ID 的幀合並為一條完整的請求信息。
  • 然后服務器處理該條請求,並將處理的響應行、響應頭和響應體分別發送至二進制分幀層。
  • 同樣,二進制分幀層會將這些響應數據轉換為一個個帶有請求 ID 編號的幀,經過協議棧發送給瀏覽器。
  • 瀏覽器接收到響應幀之后,會根據 ID 編號將幀的數據提交給對應的請求。

 

  從上面的流程可以看出,通過引入二進制分幀層,就實現了 HTTP 的多路復用技術

 

  HTTP 是瀏覽器和服務器通信的語言,在這里雖然 HTTP/2 引入了二進制分幀層,不過 HTTP/2 的語義和 HTTP/1.1 依然是一樣的,也就是說它們通信的語言並沒有改變,比如開發者依然可以通過 Accept 請求頭告訴服務器希望接收到什么類型的文件,依然可以使用 Cookie 來保持登錄狀態,依然可以使用 Cache 來緩存本地文件,這些都沒有變,發生改變的只是傳輸方式。這一點對開發者來說尤為重要,這意味着不需要為 HTTP/2 去重建生態,並且 HTTP/2 推廣起來也相對更輕松了。

 

HTTP/2 其他特性

  通過上面的分析,知道了多路復用是 HTTP/2 的最核心功能,它能實現資源的並行傳輸。多路復用技術是建立在二進制分幀層的基礎之上。其實基於二進制分幀層,HTTP/2 還附帶實現了很多其他功能,下面就來簡要了解下。

  1. 可以設置請求的優先級

  瀏覽器中有些數據是非常重要的,但是在發送請求時,重要的請求可能會晚於那些不怎么重要的請求,如果服務器按照請求的順序來回復數據,那么這個重要的數據就有可能推遲很久才能送達瀏覽器,這對於用戶體驗來說是非常不友好的。

  為了解決這個問題,HTTP/2 提供了請求優先級,可以在發送請求時,標上該請求的優先級,這樣服務器接收到請求之后,會優先處理優先級高的請求。

  2. 服務器推送

  除了設置請求的優先級外,HTTP/2 還可以直接將數據提前推送到瀏覽器。你可以想象這樣一個場景,當用戶請求一個 HTML 頁面之后,服務器知道該 HTML 頁面會引用幾個重要的 JS 文件和 CSS 文件,那么在接收到 HTML 請求之后,附帶將要使用的 CSS 文件和 JavaScript 文件一並發送給瀏覽器,這樣當瀏覽器解析完 HTML 文件之后,就能直接拿到需要的 CSS 文件和 JS 文件,這對首次打開頁面的速度起到了至關重要的作用。

  3. 頭部壓縮

  無論是 HTTP/1.1 還是 HTTP/2,都有請求頭和響應頭,這是瀏覽器和服務器的通信語言。HTTP/2 對請求頭和響應頭進行了壓縮,你可能覺得一個 HTTP 的頭文件沒有多大,壓不壓縮可能關系不大,但你這樣想一下,在瀏覽器發送請求的時候,基本上都是發送 HTTP 請求頭,很少有請求體的發送,通常情況下頁面也有 100 個左右的資源,如果將這 100 個請求頭的數據壓縮為原來的 20%,那么傳輸效率肯定能得到大幅提升。

 

總結

  首先分析了影響 HTTP/1.1 效率的三個主要因素:TCP 的慢啟動、多條 TCP 連接競爭帶寬和隊頭阻塞。

  接下來分析了 HTTP/2 是如何采用多路復用機制來解決這些問題的。多路復用是通過在協議棧中添加二進制分幀層來實現的,有了二進制分幀層還能夠實現請求的優先級、服務器推送、頭部壓縮等特性,從而大大提升了文件傳輸效率。

  HTTP/2 協議規范於 2015 年 5 月正式發布,在那之后,該協議已在互聯網和萬維網上得到了廣泛的實現和部署。從目前的情況來看,國內外一些排名靠前的站點基本都實現了 HTTP/2 的部署。使用 HTTP/2 能帶來 20%~60% 的效率提升,至於 20% 還是 60% 要看優化的程度。總之,我們也應該與時俱進,放棄 HTTP/1.1 和其性能優化方法,去“擁抱”HTTP/2。

 

思考時間

  雖然 HTTP/2 解決了 HTTP/1.1 中的隊頭阻塞問題,但是 HTTP/2 依然是基於 TCP 協議的,而 TCP 協議依然存在數據包級別的隊頭阻塞問題,那么你覺得 TCP 的隊頭阻塞是如何影響到 HTTP/2 性能的呢?

 

記錄

 老師好,采用了 HTTP/2 之后,雪碧圖是不是徹底不需要了呢?而且多張圖片變成雪碧圖后,多張圖片大小加和都沒有一張雪碧圖大,那是不是雪碧圖反而讓傳輸更慢了呢?

老師回復:http/2 是沒必要用雪碧圖了。

 

老師,還是沒有理解 http2 怎么就能解決隊頭阻塞問題呢?http2 還是基於 tcp 連接的,經過二進制分幀層了以后不還是需要以數據包的形式通過 tcp 傳輸嗎?tcp 的數據包隊頭阻塞發生了不還是會影響后面的請求數據包的發送嗎?

老師回復:是不管 http/1 還是 http/2, 最后都需要經過tcp包的形式進行傳輸!而tcp 包也是按照順序的,一個阻塞了,會影響到其他數據包的接收。

 

第一:http2 解決的是 http1.1 的隊頭阻塞問題的(一個 request 發出后,直到有了response后,才能發出下一個request),http/2 發完一個request就可以馬上發第二個request,而且第二個的response 就有可能先回來。

第二:沒有解決tcp隊頭阻塞的問題(也解決不了,因為tcp在下層)。

 

有個疑問想請教老師,既然HTTP1.1為了並行下載資源為每個域名提供了6個TCP連接,那這6個TCP連接是並行傳輸數據的么?如果是為什么還會有隊頭阻塞的問題呢?這里沒搞明白,或者其他同學明白的幫忙回答一下唄,感謝感謝

老師回復:這六個TCP連接的請求過程是並行的,這里所提到的對頭阻塞是指發生在每個單獨的tcp連接中,因為一個tcp連接可以用來處理多個http請求。

 

tcp的隊頭阻塞雖然是缺點,但也從另一個方面保證了數據傳輸的可靠性,前一個沒有完成或者出錯,可以重傳。改用udp后可能會丟幀,不可靠

作者回復: http3中的quic協議也提供了udp的可靠傳輸

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM