HTTP/2 與 WEB 性能優化(三)
在連續寫了兩篇關于「HTTP/2 與 WEB 性能優化」的文章后,今天來寫這個系列的***一篇。在正式開始之前,我們先來簡單回顧下之前兩篇文章:
「HTTP/2 與 WEB 性能優化(一)」的結論是:HTTP/2 的 Server Push 機制,可以讓重要的 JS、CSS 等資源盡快加載,從而不再需要 HTTP/1 中「將重要資源內聯在頁面頭部」的優化方案了。
「HTTP/2 與 WEB 性能優化(二)」的結論是:HTTP/2 支持了多路復用,HTTP 連接變得十分廉價,之前為了節省連接數所采用的類似于「資源合并、資源內聯」等優化手段不再需要了。多路復用可以在一個 TCP 連接上建立大量 HTTP 連接,也就不存在 HTTP 連接數限制了,HTTP/1 中常見的「靜態域名」優化策略不但用不上了,還會帶來負面影響,需要去掉。另外,HTTP/2 的頭部壓縮功能也能大幅減少 HTTP 協議頭部帶來的開銷。
但 HTTP/2 并不是***的,并不是說用了 HTTP/2 就不再需要性能優化了。我在本系統第二篇文章末尾寫到:
據官方預測,HTTP/1 至少還需要 10 年才能徹底退出歷史舞臺,另外盡管 HTTP/2 協議允許脫離 TSL 部署,但 Chrome 和 Firefox 都表示不支持非 TLS 的 HTTP/2,之后很可能一個網站會同時提供 HTTP/1.1、HTTP/1.1 over TLS、HTTP/2 over TLS 三種服務。如何在每種情況下,都能給用戶提供***的體驗,需要更加深入的優化研究和更加精細的優化策略。
實際上,除了前兩篇文章中提到的這些需要為 HTTP/2 做出調整的優化策略之外,其余大部分 HTTP/1 時期的優化策略依然有效。HTTP/1 的 WPO 并不是什么新鮮話題,大家早就熟門熟路了,本文只打算列舉其中幾個:
啟用壓縮
壓縮的目的是讓傳輸的數據變得更小。我們的線上代碼(JS、CSS 和 HTML)都會做壓縮,圖片也會做壓縮(PNGOUT、Pngcrush、JpegOptim、Gifsicle 等)。對于文本文件,在服務端發送響應之前進行 GZip 壓縮也很重要,通常壓縮后的文本大小會減小到原來的 1/4 - 1/3。對代碼進行內容壓縮已經有成熟的工具和標準流程了,而服務端的 GZip 更是標配,所以「壓縮」是一項收益投入比很高的優化手段。
使用 HTTP 緩存
任何一個 WEB 項目,要提高性能,各個環節的緩存必不可少。利用好 HTTP 協議的緩存機制,可以大幅減少傳輸數據,減少請求,這又是一項收益投入比超高的優化手段。這里把之前我寫的 HTTP/1.1 緩存機制介紹翻出來:
首先,服務端可以通過響應頭里的 Last-Modified(***修改時間) 或者 ETag(內容特征) 標記實體。瀏覽器會存下這些標記,并在下次請求時帶上 If-Modified-Since: 上次 Last-Modified 的內容 或 If-None-Match: 上次 ETag 的內容,詢問服務端資源是否過期。如果服務端發現并沒有過期,直接返回一個狀態碼為 304、正文為空的響應,告知瀏覽器使用本地緩存;如果資源有更新,服務端返回狀態碼 200、新的 Last-Modified、Etag 和正文。這個過程被稱之為 HTTP 的協商緩存,通常也叫做弱緩存。
可以看到協商緩存并不會節省連接數,但是在緩存生效時,會大幅減小傳輸內容(304 響應沒有正文,一般只有幾百字節)。另外為什么有兩個響應頭都可以用來實現協商緩存呢?這是因為一開始用的 Last-Modified 有兩個問題:1)只能精確到秒,1 秒內的多次變化反映不出來;2)在輪詢的負載均衡算法中,如果各機器讀到的文件修改時間不一致,有緩存無故失效和緩存不更新的風險。HTTP/1.1 并沒有規定 ETag 的生成規則,而一般實現者都是對資源內容做摘要,能解決前面兩個問題。
另外一種緩存機制是服務端通過響應頭告訴瀏覽器,在什么時間之前(Expires)或在多長時間之內(Cache-Control: Max-age=xxx),不要再請求服務器了。這個機制我們通常稱之為 HTTP 的強緩存。
一旦資源***強緩存規則后,再次訪問完全沒有 HTTP 請求(Chrome 開發者工具的 Network 面板依然會顯示請求,但是會注明 from cache;Firefox 的 firebug 也類似,會注明 BFCache),這會大幅提升性能。所以我們一般會對 CSS、JS、圖片等資源使用強緩存,而入口文件(HTML)一般使用協商緩存或不緩存,這樣可以通過修改入口文件中對強緩存資源的引入 URL 來達到即時更新的目的。
這里也解釋下為什么有了 Expire,還要有 Cache-Control。也有兩個原因:1)Cache-Control 功能更強大,對緩存的控制能力更強;2)Cache-Control 采用的 max-age 是相對時間,不受服務端 / 客戶端時間不對的影響。
另外關于瀏覽器的刷新(F5 / cmd + r)和強刷(Ctrl + F5 / shift + cmd +r):普通刷新會使用協商緩存,忽略強緩存;強刷會忽略瀏覽器所有緩存(并且請求頭會攜帶 Cache-Control:no-cache 和 Pragma:no-cache,用來通知所有中間節點忽略緩存)。只有從地址欄或收藏夾輸入網址、點擊鏈接等情況下,瀏覽器才會使用強緩存。
減少 DNS 查詢
我們知道,建立 TCP 連接需要知道目標 IP,而絕大部分時候給瀏覽器的是域名。瀏覽器需要先將域名解析為 IP,這個過程就是 DNS 查詢,一般需要幾毫秒到幾百毫秒,移動環境下會更慢。DNS 解析完成之前,請求會被 Block。瀏覽器一般都會緩存 DNS 查詢結果,頁面使用的域名(包括子域名)越少,花費在 DNS 查詢上的開銷就越小。另外,合理使用瀏覽器的 DNS Prefetching 技術,也是很好的做法。
減少重定向
無論是通過服務端響應頭產生的重定向,還是通過 或者 JS 產生的重定向,都可能引入新的 DNS 查詢、新的 TCP 連接以及新的 HTTP 請求,所以減少重定向也很重要。瀏覽器基本都會緩存通過 301 Moved Permanently 指定的跳轉,所以對于***性跳轉,可以考慮使用狀態碼301。對于啟用了 HTTPS 的網站,配置 HSTS 策略,也可以減少從 HTTP 到 HTTPS 的重定向。
WEB 性能優化是一個系統工程,不可能在這一篇文章里寫完,我決定先就寫到這兒。***,推薦一個 Chrome 擴展:HTTP/2 and SPDY indicator,它可以在地址欄顯示當前網站是否啟用了 SPDY 或者 HTTP/2,點擊圖標可以直接打開 Chrome 的 HTTP/2 的調試界面,十分方便。