原文地址:HTTP/2 Prioritization
原文作者:[Patrick Meenan]
譯文出自:FE-star/speed
譯者:smallbonelu
以正確的順序請求頁面資源對於快速的使用者體驗至關重要。想象一下,如果一個網頁上有一堆圖片,還有一個外部樣式表,一些自定義Web字型和一些在head
中的指令碼。如果瀏覽器首先下載了所有圖片並且最後載入了樣式表,在所有內容都載入完畢前,頁面將完全是空白頁。如果瀏覽器首先載入了所有阻塞資源,接著是Web字型和圖片,那麼它可以更早地呈現頁面,並讓使用者開始看到內容,同時載入其餘的圖片。我在Chrome瀏覽器效能工作上的大部分時間都花在了嘗試優化載入資源的順序以獲得最佳使用者體驗上。
使用HTTP/1.x
,瀏覽器可以完全控制資源載入順序。每個連線一次只能支援一個資源請求,伺服器會盡快返回請求的內容。瀏覽器可以通過決定何時請求資源以及開啟多少個並行連線來安排請求。
HTTP/2
讓這些事情變得更好也更復雜了。瀏覽器可以一次請求多個資源,指定一些優先順序資訊來幫助確定應該如何處理這些資源,然後等待伺服器發回所有資料,而不是一次請求一個。如果瀏覽器和伺服器都支援優先順序,則應使用瀏覽器指定的規則並使用所有可用頻寬來傳遞資源,而不會有資源之間的相互競爭。
每個資源都獲取一個stream ID
來標識連線上的資源,並且有三個引數用於定義資源優先順序:
- 父級資料流(Parent Stream):這個資料流是一個“依賴”資源或者應該在之後被傳遞的資料流。有一個所有資料流共享的虛擬
root stream 0
。 - 權重(Weight):1到256之間的數字,用於標識在多個資料流共享連線時分配給此資料流的頻寬量。頻寬是相對於所有其他活動的資料流的權重分配的,而不是絕對值。
- 獨佔位(Exclusive bit):一個標誌,表示應該在不與任何其他資料流共享頻寬的情況下下載。
瀏覽器不一定同時知道所有資源,因此伺服器能夠在新請求到達時重新確定請求的優先順序也很關鍵。
那麼......我們是怎麼做的呢?
瀏覽器引擎
Chrome
這包括使用Chromium的優先順序邏輯和網路堆疊的所有內容。
Chrome是唯一使用獨佔位的瀏覽器,它在每個資源上使用它。它構建了一長串資源,將較低優先順序的資源連結到仍在等待的最後一個相同或更高優先順序的資源。如果沒有更高優先順序的資源掛起,則啟動新鏈。權重以靜態對映的方式分配,Chrome的五個內部優先順序分別對應相應的權重(即HIGHEST為256)。
假設給定幾個請求佇列,所有請求都設定了獨佔位,伺服器將選擇權重最高的一個,完成傳遞後,就將其從列表中彈出並重新選擇。
假設Chrome正確構建了排序,這可能非常有效。獨佔下載資源是樣式表和指令碼等阻塞資源的最佳選擇。當涉及到圖片和視訊時,你可能需要一些交錯執行的任務(特別是對於漸進式圖片)。否則,在轉到下一個請求前,你將要等待每個圖片或視訊完全下載下來。
Firefox
Firefox實現了HTTP/2
的樹結構,並構建了一個虛擬的資料流樹,用來對不同請求型別進行分組。Firefox對分組進行了加權,以便為更重要的組提供更多的頻寬,並且當所有節點都已完成時,空閒週期(idle cycles)可用於響應。
在根級別中,有一個“leader”組,,它的頻寬是“Other group”的兩倍。在“leader”組中,有一個叫“follower”的子組,它只有在“leader”組的直系後代完成下載後才會開始下載。例如,在權重為200的“leader”組下面,一旦所有CSS完成下載後,圖片和字型才會開始下載。一個組內的所有子資源具有相同的權重並均勻分配頻寬(同時下載所有圖片或所有指令碼)。
從整體結構上來看Firefox非常出色,可以為推測請求定義空閒週期,但對同時下載所有資源優先順序的劃分並不是很好。像樣式表和指令碼等這樣的阻塞資源的優先下載要比按順序下載它們才能讓解析器處理文件要好。
Safari
這包括iOS上的所有瀏覽器(包括Chrome)。
Safari採用了一種非常簡單的方法,看起來是SPDY優先順序的遺留部分。五個內部webkit優先順序靜態對映到權重,並且沒有定義依賴關係。所有請求基於每個資源的優先順序來劃分頻寬權重進行同時下載(例如,指令碼獲得圖片頻寬的三倍)。
這樣導致高併發性並不是很好,而且落後於Firefox的實現,在Firefox中至少follower組的資源會等到leader組資源全部完成任務之後(儘管它可能不足以證明Firefox中樹的複雜性)。
Microsoft Edge / Internet Explorer
簡而言之,Microsoft Edge(和Internet Explorer)根本不支援優先順序。所有請求均勻分配頻寬(幾乎是最糟糕的情況)。
Servers/Hosting/CDNs
現在大多數伺服器都支援HTTP/2
了,通常也"支援"優先順序的。支援加了引號,是因為即使一個伺服器內部支援資源優先順序,實際上讓它能夠與瀏覽器工作也需要調整網路堆疊並儘可能減少輸出緩衝而不影響吞吐量。
緩衝可能是一個問題,因為伺服器可以傳送一堆低優先順序的響應,這些響應在高優先順序響應到來之前已經在緩衝區中排隊。傳送高優先順序響應時,無法搶佔已緩衝的低優先順序響應。緩衝可以來自伺服器本身,TLS層,TCP傳送的緩衝,甚至來自網路上的bufferbloat,跟蹤並消除所有多餘的緩衝可能會很複雜。我在今年早些時候的一篇博文中談到了一些原因和解決方案,但這並不是一個詳盡的清單。
為了測試伺服器優先順序的有效性,我構建了一個測試頁面,你可以在你的服務堆疊上部署該測試頁面以檢視優先順序是否正常工作。它專門針對Chrome的優先順序邏輯,因此最好使用慢速連線上的Chrome進行測試。它先將3MB低優先順序圖片排隊,然後在下載並執行高優先順序指令碼後,指令碼會傳送4個高優先順序請求(一張圖片,一個頁面背景,一個自定義的webfont和一個阻塞指令碼)。當優先順序正常工作時,後置的高優先順序請求的資源會跳過低優先順序請求並快速得到響應:
當優先順序工作不正常時,部分或全部後置的高優先順序請求的資源會被延遲,直到優先順序較低的請求完成為止:
後置請求的阻塞指令碼的延遲超出了“DOM Content Loaded”的度量值,字型和2個圖片的延遲對視覺體驗產生了相當大的影響:
我們從哪裡開始?
為了跟蹤CDN和託管服務提供商支援HTTP/2
優先順序的程度,Andy Davies建立了一個GitHub倉庫,用於跟蹤當前的支援狀況,任何人都可以提交測試結果來群策群力。在撰寫本文時,情況非常糟糕,只有兩個CDN確實正確地確定了優先順序,並且存在一些非常令人震驚的失敗(例如每個雲提供商甚至Google的GFE)。希望通過提高對這種情況的認知,我們將能夠為優先順序提供更廣泛的支援。
對託管和伺服器來說,好訊息是你總是可以在它們之前配置一個支援優先順序的CDN來解決問題(儘管直接支援它會很好)。
在瀏覽器方面,除了敦促瀏覽器廠商以獲得更好的支援之外,沒有太多可以做的事情。其中的一些廠商可能會遇到架構問題,如他們的瀏覽器引擎在作業系統的網路堆疊層之上,導致無法傳遞優先順序資訊。可能是我的偏見,但我認為Chrome是最接近“正確”的做法,但仍有相當大的改進空間。
HTTP/3
也即將到來,但目前的優先順序方案不會改變。這個改變是網路堆疊的終結。在伺服器端,這意味著作業系統的緩衝和擁塞控制不再起作用,伺服器軟體100%負責最小化緩衝(包括擁塞控制演算法以最小化緩衝區)。
那麼就說到這裡,希望為了HTTP/2
和一個安全,高效能的網路,我們可以在2019年修復資源優先順序。