原文地址:https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/
我們很高興地宣布,2018年2月20日發布的NGINX 1.13.9支持HTTP / 2服務器推送。對於NGINX Plus用戶,即將發布的NGINX Plus R15版本將包含HTTP / 2服務器推送支持,計划於2018年4月發布。
HTTP / 2規范中定義的服務器推送允許服務器搶先將資源推送到遠程客戶端,預計客戶端可能很快會請求這些資源。通過這樣做,您可以在頁面加載操作中將RTT(往返時間 - 請求和響應所需的時間)減少一個RTT或更多,從而為用戶提供更快的響應。
服務器推送可用於為客戶提供樣式表,圖像以及呈現網頁所需的其他資源。您應該注意只推送所需的資源; 不要推送客戶端可能已經緩存的資源。
在這篇博文中,我描述了:
- HTTP / 2服務器推送的基本配置
- 如何驗證HTTP / 2服務器推送是否正常(使用瀏覽器工具或
nghttp
) - 使用
Link
標題自動推送內容 - 有選擇地推送內容
- 衡量HTTP / 2服務器推送的效果
配置HTTP / 2服務器推送
要將資源與頁面加載一起推送,請http2_push
按如下所示使用該指令:
server {
# Ensure that HTTP/2 is enabled for the server
listen 443 ssl http2;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
# whenever a client requests demo.html, also push
# /style.css, /image1.jpg and /image2.jpg
location = /demo.html {
http2_push /style.css;
http2_push /image1.jpg;
http2_push /image2.jpg;
}
}
驗證HTTP / 2服務器推送
您可以使用以下兩種方法之一輕松驗證服務器推送是否有效:
- Web瀏覽器中的開發人員工具
- 一個命令行的HTTP / 2客戶端如
nghttp
使用開發人員工具進行驗證(Google Chrome)
以下以Google Chrome為例,說明如何使用Web瀏覽器中的開發人員工具來驗證服務器推送是否有效。在該圖中,Chrome開發人員工具的“ 網絡”標簽上的“ 啟動器”列指出,作為/demo.html請求的一部分,有多個資源被推送到客戶端。
啟動器列表示服務器推送用於發送資源
使用命令行客戶端進行驗證(nghttp
)
除了Web瀏覽器工具,您還可以使用nghttp2.org項目中的nghttp
命令行客戶端來驗證服務器推送是否有效。您可以從GitHub下載命令行客戶端,或在可用的位置安裝相應的操作系統軟件包。對於Ubuntu,請使用該軟件包。nghttp
nghttp2-client
在輸出中,星號(*)表示服務器推送的資源。
$ nghttp -ans https://example.com/demo.html id responseEnd requestStart process code size request path 13 +84.25ms +136us 84.11ms 200 492 /demo.html 2 +84.33ms * +84.09ms 246us 200 266 /style.css 4 +261.94ms * +84.12ms 177.83ms 200 40K /image2.jpg 6 +685.95ms * +84.12ms 601.82ms 200 173K /image1.jpg
自動將資源推送給客戶
在很多情況下,列出您希望推送到NGINX配置文件中的資源是不方便的,甚至是不可能的。出於這個原因,NGINX也支持攔截Link
預加載頭的約定,然后推送這些頭中標識的資源。要啟用預加載,請http2_push_preload
在配置中包含指令:
server { # Ensure that HTTP/2 is enabled for the server listen 443 ssl http2; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # Intercept Link header and initiate requested Pushes location = /myapp { proxy_pass http://upstream; http2_push_preload on; } }
例如,當NGINX作為代理服務器(用於HTTP,FastCGI或其他流量類型)運行時,上游服務器可以Link
為其響應添加如下標題:
Link: </style.css>; as=style; rel=preload
NGINX攔截這個頭並開始服務器推送/style.css。Link
標題中的路徑必須是絕對路徑 - 不支持像./style.css這樣的相對路徑。該路徑可以選擇包含查詢字符串。
要推送多個對象,可以提供多個Link
標題,或者更好的是,將所有對象包含在逗號分隔的列表中:
Link: </style.css>; as=style; rel=preload, </favicon.ico>; as=image; rel=preload
如果你不想讓NGINX推送一個預先加載的資源,請nopush
在頭文件中添加一個參數:
# Resource is not pushed Link: </nginx.png>; as=image; rel=preload; nopush
當http2_push_preload
啟用時,您還可以通過在NGINX配置中設置響應標題來啟動預加載服務器推送:
add_header Link "</style.css>; as=style; rel=preload";
有選擇地向客戶推送資源
HTTP / 2規范沒有解決確定是否推送資源的挑戰。顯然,如果你知道他們可能需要這些資源並且不太可能已經緩存了資源,那么最好只將資源推送給客戶端。
一種可能的方法是僅在首次訪問該網站時將資源推送給客戶。例如,您可以測試會話cookie的存在情況,並Link
有條件地設置標題,以便僅在會話cookie不存在時才預加載資源。
假設客戶端運行良好,並在隨后的請求中包含cookie,使用以下配置,NGINX每次瀏覽器會話僅向客戶端推送一次資源:
server { listen 443 ssl http2 default_server; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; http2_push_preload on; location = /demo.html { add_header Set-Cookie "session=1"; add_header Link $resources; } } map $http_cookie $resources { "~*session=1" ""; default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=style; rel=preload"; }
測量HTTP / 2服務器推送的效果
為了衡量服務器推送的效果,我們創建了一個簡單的測試頁面/demo.html,它引用了一個單獨的樣式表/style.css。樣式表進一步引用兩個圖像。我們使用三種不同的配置測試了頁面加載時間:
- 連續
GET
小號(無優化) -瀏覽器加載的資源時,才發現他們被要求 - 預加載提示 - 預加載提示(
Link
標題)包含在第一個響應中以告訴瀏覽器加載依賴關系 - 服務器推送(僅限HTTP / 2) - 依賴性被先發送到瀏覽器
測試了三種配置,以測量服務器推送時HTTP / 2的影響
我們使用HTTP,HTTPS或HTTP / 2對每個配置進行了多次測試運行。前兩種配置適用於所有三種協議,並且服務器僅推送到HTTP / 2。
該行為是使用Chrome開發人員工具測量的。評估每個配置的最常見行為並對其進行平均,並將時間與鏈路的RTT(如使用所測量的ping
)相關聯以說明每種方法的機械效應。
顯示許多往返行程的測試結果是在每種配置下發生的
一些基本觀察
- 已加載DOM是啟動新連接並檢索demo.html頁面的時間。樣式表是檢索CSS資源的時間。
- 通過HTTP建立連接需要1個RTT; 對於HTTPS和HTTP / 2,它需要2個RTT。
- HTML和CSS資源的有效負載小於最大傳輸單元(MTU)大小,因此
GET
操作在大約1個RTT中完成。
解讀結果:預加載提示
- 預加載提示對CSS資源的影響很小,因為它是在HTML資源中直接引用的,並且HTML資源可以快速傳遞。瀏覽器在HTML頁面交付后立即啟動CSS請求。
- 預加載提示確實可以快速開始下載在CSS資源(這里是兩個圖像)中聲明的資源。使用預加載提示時,下載可以更快地啟動1 RTT。
- 預加載提示的凈節省可以是1 RTT或更多。在並行下載資源時,瀏覽器必須打開一個或多個其他連接。性能取決於緩慢的請求(較大的響應)是先排定還是延遲,而新的連接是打開的。這種不可預測的請求順序考慮了HTTP和HTTP / 2的1-RTT加速以及HTTPS的2-RTT加速。
解釋結果:服務器推送
- 服務器推進通過進一步的1 RTT改進了預加載提示時間。推送“響應”是在響應第一個請求的同時發起的,而預加載提示響應發生了1個RTT延遲 - 0.5個RTT用於響應第一個請求,另加0.5個RTT用於預加載
GET
請求。
測試筆記
- 每個配置都有多個測試運行。每次運行都從一個空的瀏覽器緩存開始,並且沒有建立與NGINX服務器的保持連接。NGINX指令
keepalive_timeout
並http2_idle_timeout
用於快速關閉保持連接。 - 目前似乎無法將字體資源推送到Chrome,可能是由於已知的復雜情況。Chrome甚至已經推送了一個字體資源的明確請求。
- 注意在每次測試之前明確清除瀏覽器緩存,並且所有內容都提供了過期的緩存控制標頭。
- Chrome緩存預先加載的資源。當您禁用緩存時,這些緩存的資源不會始終被忽略,並且不會通過明確的“清除瀏覽器緩存”操作得到一致的清除。(在Chrome中,禁用緩存的一種方法是在開發工具網絡選項卡上選中禁用緩存復選框。要清除瀏覽器緩存,可以在打開開發工具並右鍵單擊瀏覽器刷新按鈕並選擇清空緩存和硬重新加載) 。
- 一些嘗試預加載內容導致Chrome無法成功重新驗證緩存副本,然后正常下載資源。這些嘗試不計入測量中。
- Chrome為所有使用先前接受的自簽名證書的新SSL連接添加了不必要的2-RTT延遲。該測試使用CA簽名的Let's Encrypt證書進行,以避免此2-RTT延遲。
- DNS延遲通過編輯本地/ etc / hosts文件解決。
- 這個簡單的測試並不試圖測量客戶端已經擁有資源緩存副本的服務器推送效果。在這種情況下,在每次測試之前,所有高速緩存都已被清除,而且大多數推送都很快取消。
結論
為了突出預加載提示和服務器推送的機制,此測試非常簡單。與簡單情況下的預裝載提示相比,服務器推送改進了1-RTT,與未優化的順序GET
請求和從屬資源的發現相比,服務器推送有了更大的改進。
更實際的用例有更多的變量:多個依賴資源,多個資源,甚至通過推送已經緩存或不需要立即需要的資源來浪費帶寬。瀏覽器不一致還會影響性能。從這個簡單的測試你的里程將會有所不同。
例如,Chrome團隊已經發布了關於何時部署服務器推送的一些詳細建議,並且已經在更復雜的網站上進行了測量,以比較未優化,預加載提示和服務器推送HTTP / 2的影響。對於考慮在生產中部署HTTP / 2服務器推送的人來說,他們的HTTP / 2推送報告的經驗法則值得一讀。
務實的結論是,如果您可以預先確定需要哪些資源,那么讓上游服務器發送預加載提示會帶來真正的好處。推動這些資源的額外好處雖然很小但是可以衡量,但可能會導致浪費帶寬和延遲所需資源。您應該仔細測試和監控任何服務器推送配置。
附錄:HTTP / 2推送如何工作?
以下信息部分基於Jake Archibald的非常詳細的HTTP / 2推送研究,比我認為的博客文章更加艱難。
HTTP / 2服務器推送通常用於在客戶端請求資源時搶先發送從屬資源。例如,如果客戶端請求網頁,則服務器可能會將相關樣式表,字體和圖像推送到客戶端。
當客戶端進行HTTP / 2連接時,服務器可以選擇使用連接發起一個或多個推送響應。這些推送發送客戶端未明確請求的資源。
客戶端可以拒絕推送(通過發送RST_STREAM
幀)或接受推送。客戶端將推送的內容存儲在與HTTP / 2連接關聯的本地“推送緩存”中。
稍后,當客戶端使用已建立的HTTP / 2連接請求資源時,它將檢查連接的推送緩存,以查看是否已完成或正在傳輸對請求的響應。它首先使用緩存資源來為資源發出新的HTTP / 2請求。
任何推送的資源都會保留在每個連接推送緩存中,直到(a)它被使用或者(b)HTTP / 2連接關閉:
- 如果使用資源,客戶端將獲取副本並刪除推送緩存中的條目。如果資源可緩存,則客戶端可以將其副本緩存在其HTTP頁面緩存中。
- 如果因任何原因關閉了HTTP / 2連接,則會刪除其本地推送緩存。
這有幾個含義:
- 即使推送的內容是新鮮的,瀏覽器中的HTTP頁面緩存中的內容也優先於推送緩存中的內容。
- HTTP / 2連接可以在不同的頁面加載中共享。由於一次頁面加載而推送的資源可以在不同頁面加載請求時使用。
- 具有憑證的請求使用不具有憑證的請求的不同HTTP / 2連接; 例如,如果瀏覽器為資源發出非憑證請求,則可能無法找到使用跨源請求推送的資源(憑證)。
您可以在Jake Archibald的HTTP / 2推送比我想到的博客文章更加強硬的問題中查看更詳細的問題列表。
HTTP / 2服務器推送是一個有趣的功能。確保徹底測試HTTP / 2服務器推送配置,並准備在出現更可預測的緩存感知行為的情況下回退到預加載。