[譯] HTTP/2 下提高網站載入速度的資源打包指南

yct21發表於2017-10-30

HTTP/2 下提高網站載入速度的資源打包指南

載入速度一直是 web 開發的重點。隨著 HTTP/2 的出現,我們用很少的精力就可以提升網站效能。本文首先為不熟悉的讀者簡要介紹了 HTTP/2 的基本概念,隨後列出了基準測試時所得到的資料,並在此基礎上給出了一些簡潔的參考建議,確保網站對 HTTP/2 進行了優化。

HTTP/2 的概念與重要性

自 1997 年誕生以來,HTTP/1.1 一直以一成不變的方式工作著,但網站卻日漸臃腫起來。終於,在 2015 年,HTTP 協議迎來了大版本更新。HTTP/2 更加註重頁面載入的延遲,畢竟使用者對一個 web 應用的效能的感受,取決於延遲而不是頻寬。HTTP/2 採用了多路複用的工作方式,輔以首部壓縮等手段,以解決延遲的問題。

在精心設計下,HTTP/2 的語義相容於 HTTP/1.1。站在開發者的角度,我們無需因此改變寫程式碼的方式。不過在 HTTP/2 的環境下,為了最大化多路複用所能帶來的效能提升,我們需要對傳輸資原始檔的方法做出調整。隨著 HTTP/2 這門技術的流行以及支援它的主機服務商的增加,優化網站在 HTTP/2 下的效能,從而在永不停息的網站速度競賽中保持競爭力,顯得尤為重要。根據 W3Techs 的統計,2016 年 4 月,全球流量前一千萬的網站中,已經有 7.1% 的網站支援 HTTP/2,這個資料還在不斷上升。同時,CloudFlare 發表了一篇有意思的文章,介紹了 SPDY & HTTP/2 在現實世界的流量統計。其中指出,早在 2015 年的 12 月,就已經有 81% 的網路流量,是以 SPDY 或者 HTTP/2 的方式進行傳輸的。

考慮到 SPDY 作為 HTTP/2 的先驅,提供了絕大部分 HTTP/2 能帶來的優化,因此本文將其視為 HTTP/2 的變種。而若將 SPDY 與 HTTP/2 放在一起統計,我們可以看到,HTTP/2 已經不是未來的概念,而是現已成熟的技術。它已無所不在,絕大部分的桌面或移動瀏覽器,都至少支援 SPDY 和 HTTP/2 其中的一個。

HTTP/2 打包的謠言

所以要最大化 HTTP/2 的收益,我們到底應該怎樣組織網站的資原始檔?在 HTTP/1.1 的時代,將多個資原始檔拼成一個大檔案來減少總連線數,是對網站效能最重要的提升。

這種方法的壞處是,瀏覽器的快取管理功能會受到影響。哪怕一個很小的資原始檔發生了變動,整個合併後的檔案都要再重新傳輸一遍。當然在 HTTP/1.1 的環境下,拼接檔案所帶來的效能提升,要遠遠高於其帶來的損失。

而另一方面,HTTP/2 可以同時傳輸多個小檔案,無需過多的額外開銷。因此,在 HTTP/2 環境下,合併資原始檔一直被視為錯誤的做法。畢竟,避免了合併檔案,可以讓瀏覽器的快取更加有效地運作。

然而在實際環境中,我們發現事情並不簡單

和主流觀念不同,我們的基準測試顯示,合併資原始檔這種做法即使是在 HTTP/2 下也能提升網站效能。和 HTTP/1.1 下拼接為一個檔案的策略不同,HTTP/2 中更好的方法是將資源分組,分別打包。這種做法不僅能減少延遲,還可以發揮瀏覽器快取管理的作用。而即使客戶端只支援 HTTP/1.1,這裡也只是從傳輸一個合併檔案變為傳輸多個打包檔案,頁面載入的表現不會受到太大影響。

HTTP/2 基準測試的細節

這裡的基準測試每次都使用 4 個頁面,各個頁面會從伺服器請求並載入不同數量的 JavaScript 檔案,用以模擬檔案合併的不同層級。每次測試所用的 JavaScript 檔案數量各有不同,但傳輸的資料總量保證是一致的。

測試選取了搭建於 3 個不同地區的 AWS 上的 web 伺服器,用以模擬客戶端與伺服器間在不同距離上的連線。客戶端採用了 15Mbps 的 Comcast 民用光纖寬頻,使用 Windows 10 和 Chrome 50 進行測試。

後文的測試結果圖表中,4 列代表 4 種不同的檔案合併級別,每一行給出的資料分別是對 10 中不同的樣本資料進行測試的結果(關閉了瀏覽器快取),每次測試之間會有一段間隔時間。

  • 1000:載入 1000 個小型 JavaScript 檔案,每個 819 位元組,用以模擬不做任何合併的策略。
  • 50:載入 50 箇中性 JavaScript 檔案,每個 16399 位元組,用以模擬中度的合併策略。
  • 6:載入 6 個大型 JavaScript 檔案,每個 136666 位元組,用以模擬較激進的合併策略。
  • 1:載入 1 個巨型 JavaScript 檔案,每個 819999 位元組,用以模擬極端的合併策略。

從聖荷西到美國西部(北加利福尼亞)

結果顯示,當檔案數量從 1000 個降到了 50 後,載入速度平均上升了 66%。當檔案數到達 6 個乃至 1 個時,相比於完全不做合併時的載入速度,也有幾乎 70% 的提升。

從聖荷西到美國東部(北弗吉尼亞)

在這次的基準測試中,隨著檔案數量從 1000 降至 50,載入速度平均提升了 28.4%。

從聖荷西到亞太地區(首爾)

這裡我們可以看到,由於客戶端與伺服器的距離過遠,頁面載入的速度明顯降低。但當檔案數量從 1000 降到 50 以下時,載入速度還是有平均 27% 的提升。

測試結果與重要發現

  • 即使在 HTTP/2 的環境下,每種程度的檔案合併都能帶來顯著的效能提升。
  • 如果客戶端與伺服器的距離很近(連線的延遲非常短),效能的提升相當顯著(提升了 3 倍)。
  • 1000 以下的 3 個檔案數量級別(50,6 和 1 個檔案),效能提升的差距可以忽略。
  • 隨著客戶端和伺服器的距離逐漸增加(延遲逐漸加大),所有樣本中載入速度的波動都在增加。也就是說在超長距離的測試中,由於資料的波動太大,比較任意 2 個資料可能是無意義的。

結論與建議

儘量將資原始檔打包傳輸

儘管 HTTP/2 被設計成一個可以高效傳輸許多小檔案的協議,但當需要傳輸的檔案數達到一定規模後,每個檔案帶來的額外開銷也會積少成多,影響效率。此外,瀏覽器和伺服器本來就都有並行傳輸資料流的上限。Chrome 的上限似乎是 256;而在 NGINX 中用於基準測試的 ngx_http_v2_module 裡面,會使用 http2_max_concurrent_streams 這個配置引數,它的預設值為 128。一個現代的 web 應用,如果不去做任何合併,資原始檔的數量可以輕而易舉到達好幾百,導致 HTTP/2 需要分多次進行傳輸

為了提高瀏覽器快取的效率,資原始檔要分成多個組進行打包,而不要全部合併成單獨的一個檔案。每一個包中應該包含一組相關的資源,如果這樣的話,改動就可以控制在組內而不影響其他組。

舉個例子,對於每個 NPM 模組都單獨打包,是一個不錯的策略。每當某個模組更新時,只有該模組對應的瀏覽器快取會失效。這個策略會導致打包後的檔案數量增加,但我們可以從基準測試中看出,當傳輸的檔案數量低於一個值(本次測試中的 50)時,得益於 HTTP/2 的多路複用,效能不會受到過多影響。上文已經提過,別輕信 HTTP/2 下不要做任何合併的建議。從測試結果中可以看出,不合並策略下,傳輸各個小檔案帶來的開銷,積累起來毫無疑問會影響效能。

考慮相容 HTTP/1.1

儘管 HTTP/2 (或者 SPDY) 已被廣泛使用,我們依然不能忽略 HTTP/1.1 協議。這點在垂直應用中更加重要。

為了同時保證 HTTP/2 和 HTTP/1.1 環境下的效能,最好方法是根據瀏覽器支援的協議,採取不同的檔案合併策略(對 HTTP/2 使用適度的檔案合併,對 HTTP/1.1 使用極端的檔案合併)。不過在大部分情況下,維護 2 種不同合併策略,是沒必要的過度優化。

如果我們在 HTTP/1.1 下也採取前文所建議的分組打包的策略會如何呢?

如你所見,用 HTTP/1.1 傳輸 50 個打包檔案的結果,並不會比傳輸 6 個或者 1 個檔案要差太多。因此,在 HTTP/2 和 HTTP/1.1 間找到平衡,選取一個合適的檔案打包數量,是一個合理的妥協。

注1: 在 HTTP/1.1 模式的測試中,我們使用了 Chrome 作為瀏覽器,傳輸使用的是 HTTP 而不是 HTTPS。由於 Chrome 瀏覽器在開啟 HTTP/1.1 的網站時使用 6 個併發的 TCP 連線,所以現實世界中的 HTTP/1.1 瀏覽器在資原始檔增加時,可能會遭遇效能的顯著下降。
注2:注意不要拿 HTTP/1.1 的資料直接去和之前 HTTP/2 測試中的資料進行比較。HTTP/2 的測試在繁忙的工作日裡進行,而 HTTP/1.1 測試執行於週日下午,結果會優於 HTTP/2 測試。

繼續使用雪碧圖

在 HTTP/2 環境下,出於和合並檔案同樣的理由,大家可能認為,雪碧圖 是應該避免使用的。

然而,如果雪碧圖中的每個圖示檔案都足夠小,且採用了相同的設計主題,那麼相比分別傳輸單獨的圖片檔案,使用雪碧圖可能是更好的方法。

因為如果雪碧圖中的圖示之間相互聯絡,並共享同樣的設計主題,那麼當設計發生變化時,很有可能雪碧圖中的很多圖示都需要更新,此時小粒度的快取不再有優勢。

謹慎使用 data URI

還有一種打包資原始檔的方法,是採用 data URI的形式,直接將資源內聯在網頁中。這種方法在 HTTP/2 中一般也被認為是應當避免的。

內嵌 data URI 的利弊,是一個更為微妙的問題,合適的答案應該是“看情況而定”。如果資原始檔非常小(小於 100 位元組),採用內聯更加合理。即使資原始檔相對較大,如果這些資源經常發生變動,或者這些變動必須和資源所在的頁面同步,內聯也更有利。

同時支援 SPDY 和 HTTP/2

這是一個部署問題,和開發的關係不大。如今還有很多瀏覽器只支援 SPDY,HTTP/2 對 SPDY 的徹底取代還需要一定的時間。

而在此之前,我們還不能忽視 SPDY。在部署應用時,我們要讓伺服器同時支援 SPDY 和 HTTP/2 協議。CloadFlare 開源了 一個補丁,可以讓 NGINX 做到這點。


考慮到 HTTP/2 起步不久,還沒有得出相關的最佳實踐方法。儘管如此,web 開發者最好還是留心本文提出的建議,儘可能地榨取 HTTP/2 提供的效能提升,靈活地利用好瀏覽器快取。

這裡可以檢視基準測試程式碼:github.com/gourmetjs/h…

這裡可以檢視文章中所有的圖表: github.com/gourmetjs/h…


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章