「html
互聯網工程任務組(IETF)於2014年12月將HTTP/2標準提議遞交至互聯網工程指導組(IESG)進行討論,於2015年2月17日被批准,於2015年5月以RFC 7540正式發表。-維基百科前端
回過頭來看,距離HTTP/2標準發佈已通過去了五年。這五年,前端發生了技術翻天覆地的變化: IE瀏覽器逐漸式微,MVVM框架的興起和繁盛,WebAssembly的萌芽。nginx
這五年,HTTP/2 的普及和推開也無比迅速。面試
根據最新的數據,HTTP/2的瀏覽器支持程度已經來到了使人振奮的96%。瀏覽器
「緩存
HTTP/2 相較於 HTTP1.x 有哪些改進?性能優化
上面這個問題已經成爲前端面試的必考題。二進制分幀、多路複用、頭部壓縮、服務端推送這幾個關鍵詞你們已經熟的不能再熟。服務器
HTTP/2帶來了顯而易見的性能提高,又有着如此高的瀏覽器支持度,和極低的使用成本,不用簡直天理難容。目前中國各大網站都使用 HTTP/2 提高網站加載性能和用戶體驗。markdown
但咱們再看HTTP/2的幾大特性,二進制分幀、多路複用、頭部壓縮這幾個自沒必要多說,只要開啓了HTTP/2,就可使用這些特性,給網站帶來性能優化。那麼,Server Push去哪了?cookie
咱們找遍了中國各大門戶網站,都沒有看到Server Push的身影。問題出在哪?
簡單回顧一下Server Push的概念:
「
服務器推送(server push)指的是,尚未收到瀏覽器的請求,服務器就把各類資源推送給瀏覽器。
上面這張圖片看起來很美妙,當使用Server Push時,CSS和HTML一塊兒返回,從而減小了一個RTT的時間。
讓咱們試着解決第一個問題:
爲何Server Push沒有獲得普遍的應用?
「
ServerPush是HTTP/2 協議裏面惟一一個須要開發者本身配置的功能,其餘功能都是服務器和瀏覽器自動實現,不須要開發者關心。
是不是由於須要配置,才使得Server Push難以推廣?
Nginx和Apache等網關早已支持Server Push功能,只須要簡單配置後,在HTML的Response的Header中帶上:
Link: ; rel=preload; as=style
複製代碼
即可以輕鬆實現ServerPush😁。
一兩句簡單的配置,沒法阻擋開發者對於極致性能的追求,那麼還有其餘緣由嗎?
上圖中Without Push和With Push對比只是最簡單的場景,基本不可用於實際的生產中。
在實際的場景中,爲了提高加載速度和減輕服務器的負載,通常會使用CDN進行資源的加載。因而乎,咱們的加載時序圖就會變成這樣:
彷佛也不復雜,只要咱們的CDN支持Server Push,同Nginx同樣,帶上相應的Header,即可以實現ServerPush。
繼續研究一下 ServerPush CDN的支持狀況:在2016年4月28日,國外最大的提供商cloudflare宣佈支持ServerPush。
而反觀國內,各大CDN廠商彷彿對ServerPush這一HTTP/2特性熟視無睹。騰訊雲、阿里雲兩大雲廠商CDN產品文檔都沒有相關說明。只有一家小廠」又拍雲「在2018年出過一篇PR文章:
通過重重查找,咱們發現了一些端倪:
騰訊雲+社區的一篇文章在開頭指出,騰訊雲已經支持ServerPush,而且進行了相關的性能測試。
萬事俱備,只欠東風。然而,歷史包袱是沉重的。靜態資源和HTML不一樣域讓CDN ServerPush成爲了夢。
以常見的一個源站與CDN不一樣域的頁面爲例,HTML做爲Web請求的入口,爲了不CDN Cache致使用戶沒法即時更新應用,通常選擇不走CDN,直接解析到Origin的Nginx上。
而JS、CSS、IMG等靜態資源,則是走CDN的域名。
HTML和CSS不在同一個域下,根本沒法Push。
訪問一下國內的各大網站,發現基本上都是用了這種靜態資源與HTML不一樣域的方案,要實現ServerPush,就要推進CDN主域化。
CDN主域化是一個複雜、充滿風險的工程,尤爲是在業務高速運轉時進行,無異於高速路上換輪胎。其方案要通過層層設計,本文在此就不作研究。
假設咱們已經實現了CDN主域化,Server Push是否是能夠立刻提高性能呢?
「
現階段,Server沒法得知Client是否有Cache
若是Client已經有該資源的Cache,那麼Push的靜態資源會毫無心義地浪費帶寬。雖然瀏覽器能夠經過RST_STREAM阻止Server繼續向Client發送資源,可是一部分資源已經在網絡中進行傳輸。
餓了麼的這篇文章:
給咱們帶來的比較好的啓示,文章中提到, 爲了不靜態資源和HTML不一樣域、Push Cache這兩大問題,選擇了不Push靜態資源,而是Push CGI請求。
相較於Preload CGI邏輯,ServerPush CGI請求可以減小1個RTT時間,而且不用考慮Cache的問題,從而穩定地帶來性能上的提高。
目前,IETF已經有相關草案在進行討論,使用Cache Digests,請求帶上客戶端的緩存狀況供服務器識別,從而解決ServerPush的Push Cache問題。
目前 103 Early Hints 信息狀態響應碼也一樣處於草案階段,經過簡單的HTTP回包來容許用戶在服務器還在準備響應數據的時候預加載一些資源。
相較於Server Push,它避免了Push Cache的問題,但性能上沒有Server Push極致。
下圖總結了各類方案(Preload、103 Early Hints、Server Push、Server Push + Cache Digests)的性能:
(注:不考慮HTTPS TLS握手的RTT)
能夠看到,Server Push + Cache Digests在性能上擁有很大的優點。但願它能儘快成爲規範。
Nginx Team對Server Push在實際場景下首次加載作了一次benchmark,以下所示:
原文:Introducing HTTP/2 Server Push with NGINX 1.13.9
Server Push在這種場景下也能帶來必定性能提高。
在什麼狀況下,咱們應該使用Server Push?
毫無疑問,當Client與Server的過長而帶寬十分充足時,ServerPush節省的一個RTT能帶來很好的優化。
一個新的問題是:RTT多長時,該使用ServerPush?
Google Chrome小組的一篇文章給出了計算公式:
Rules of Thumb for HTTP/2 Push
翻譯以下:
遺憾的是,咱們沒法正常得知用戶的帶寬和RTT時間。
爲了不Push Cache這一問題,咱們能夠只爲第一次訪問的用戶進行Push。
能夠考慮使用Cookie來鑑別是否爲第一次訪問的用戶,但也須要注意Cookie並不能完整的描述全部靜態資源Cache的狀況,舉個例子:
Client Cookie沒有失效,可是CSS資源的Cache已經失效,此時Server由於Client有Cookie而選擇不進行Push。
固然,也能夠設計更復雜的方案在Cookie中作標記,告訴Server Push的時機。
還好已經有方案來實現:
H2O 服務器提供了一個叫做 cache-aware server push的方案,原理就是將全部緩存過的資源都記錄在 cookie 裏,這樣服務器就知道哪些資源不須要被推送了。
不過,在 cookie 裏記錄全部的資源路徑會佔用不少的空間,所以還須要將路徑壓縮一下。
這裏能夠考慮使用bloom filter來減小cookie數據量,能夠點擊查看這篇文章:
若是應用使用了SSR,Push靜態資源可能會佔用加載HTML的帶寬,從而增長首屏時間。CSR的場景更適合Server Push。
因爲Server Push可能會佔據Client端本就窄小不充裕的帶寬,在某些場景下,可能會起到拔苗助長的效果。
另外一個緣由是Server Push使用冷TCP鏈接,TCP的慢啓動致使加載資源的效率比熱TCP連接更慢
而下一個頁面的資源由Service Worker來主動fetch相較來講要更爲合適。
Introducing HTTP/2 Server Push with NGINX 1.13.9
Rules of Thumb for HTTP/2 Push
To push, or not to push?! - The future of HTTP/2 server push - Patrick Hamann - JSConf EU 2018