Apache HTTP Server 版本 2.4
HTTP/2 是全球最成功的應用層協定 HTTP 的進化版本。它的重點在更有效率地使用網路資源。它不會改變 HTTP 的基本原理和語意。還是有要求和回應,以及標頭等等。因此,如果您已經知道 HTTP/1,就已經知道 HTTP/2 的 95% 了。
已經有許多關於 HTTP/2 及其運作的文章。最具規範性的當然是它的 RFC 7540(也有較易閱讀的格式,僅供參考)。因此,您可以在這裡找到基礎知識。
但是,RFC 實際上並不容易閱讀。最好是先了解這項功能想要做什麼,然後再讀 RFC 了解它如何做到。一篇更好的入門文件是 HTTP/2 說明,作者是 curl 的作者,Daniel Stenberg。它有越來越多語言的版本!
時間不夠,沒看完:在閱讀本文時,有一些新的專有名詞和問題要注意
HTTP/2 通訊協定由「httpd 模組」實作,恰如其名 mod_http2
。他實作 RFC 7540 所描述的一組完整功能,並支援安全文字傳輸(http:)及安全(https:)連線上的 HTTP/2。安全文字傳輸變體稱為「h2c
」,安全的稱為「h2
」。h2c
允許直接模式,以及透過初始 HTTP/1 傳輸的「Upgrade:
」。
HTTP/2 的其中一項提供網頁開發人員新功能的特色,便是 伺服器推送。請參閱那一段,了解網頁應用程式如何加以利用。
mod_http2
將 nghttp2 的函式庫做為實作基礎。為了建立 mod_http2
,您需要在系統安裝至少 1.2.1 版的 libnghttp2
。
當您使用 ./configure
配置 Apache httpd 原始碼樹時,需要給它「--enable-http2
」做為額外的引數,以觸發建立模組。如果您的 libnghttp2
位於不尋常的目錄(根據您的作業系統而定),可以使用「--with-nghttp2=<路徑>
」向 configure
通知其位置。
雖然對大多數人來說,這應該是可行的,但有些人可能偏好在此模組中使用靜態連結的 nghttp2
。對他們而言,有「--enable-nghttp2-staticlib-deps
」選項。它的運作方式與 Openssl 到 mod_ssl
的靜態連結相當類似。
說到 SSL,您需要了解到許多瀏覽器只會在 https:
的網址上使用 HTTP/2,因此您需要一個支援 SSL 的伺服器。不僅如此,您需要的 SSL 函式庫必須支援 ALPN
延伸功能。如果您使用 Openssl,您至少需要 1.0.2 版。
當您建立了包含 mod_http2
的 httpd
時,需要進行一些基本組態才能啟用它。和所有 Apache 模組一樣,您需要載入它的第一件事
LoadModule http2_module modules/mod_http2.so
您需要新增至伺服器組態的第二個指令是
Protocols h2 http/1.1
這允許 h2(安全的變體)成為您的伺服器連線上的首選通訊協定。當您想要啟用所有 HTTP/2 變體時,您只要寫
Protocols h2 h2c http/1.1
依您將此指令放置的位置,它會影響所有連線或只影響特定虛擬主機的連線。您可以像下面一樣嵌套它
Protocols http/1.1 <VirtualHost ...> ServerName test.example.org Protocols h2 http/1.1 </VirtualHost>
這讓連線只允許 HTTP/1,除了 test.example.org
的 SSL 連線,它提供 HTTP/2。
需要設定成強力的 TLS 加密模組。目前的 SSLCipherSuite
mod_http2
版本並未強制任何加密模組,但大多數客戶端會強制加密。將瀏覽器指向有啟用 h2
的伺服器,但使用不適當的加密模組,會讓它直接拒絕連線並退回 1.1 的 HTTP。這是初次為 HTTP/2 設定 httpd 時常見的錯誤,因此請記住它,以避免漫長的除錯時段!如果您想確認要選擇的加密模組,請避免 HTTP/2 TLS 拒絕清單 中所列的模組。
提及通訊協定的順序也相關。預設情況下,第一個通訊協定是最優先的通訊協定。當客戶端提供多個選擇時,會選擇最左邊的通訊協定。在
Protocols http/1.1 h2
中,最優先的通訊協定是 HTTP/1,除非客戶端只支援 h2,否則它將會一直被選取。由於我們希望與支援它的客戶端通訊中使用 HTTP/2,因此更好的順序是
Protocols h2 h2c http/1.1
排序中還有另一件要注意的事項:客戶端也有自己的首選項。如果要的話,您可以設定您的伺服器選擇客戶端最優先的通訊協定
ProtocolsHonorOrder Off
讓您所寫的通訊協定順序變得無關緊要,只有客戶端的排序會影響決定。
最後要注意的事項:您設定的通訊協定不會檢查正確性或拼寫。您可以提及不存在的通訊協定,因此無須以任何 <IfModule>
檢查保護 Protocols
。
如需有關組態的更進階提示,請參閱 模組部分中關於調整大小的說明 和 如何使用同一個憑證管理多個主機。
支援 HTTP/2 的所有多工處理模組都會附有 httpd。不過,如果您使用 prefork
mpm,會有嚴格的限制。
在 prefork
中, mod_http2
每個連線將只處理一個請求。但瀏覽器等用戶端會同時發出多個請求。如果其中一個請求花了很長的時間處理(或為長時間輪詢),則其他請求將會停止。
預設情況下,mod_http2
不會解決此問題。原因是只要您執行未準備好進行多執行緒處理的處理引擎,例如會發生故障且不只一個請求,此時只會選擇 prefork
。
如果您的設定可以處理,現今最佳的方式是設定 event
mpm(如果您的平台支援)。
如果您真的無法使用 prefork
,又想有多個請求,您可以調整 H2MinWorkers
達到這個目標。不過,如果導致中斷,則兩者之間的影響您自行負責。
幾乎所有現代瀏覽器都支援 HTTP/2,但僅在 SSL 連線中使用:Firefox (v43)、Chrome (v45)、Safari(自 v9)、iOS Safari(v9)、Opera(v35)、Chrome Android(v49)和 Internet Explorer(Windows 10 上的 v11)(來源)。
其他用戶端和伺服器會列於 實作清單 中,其中包含 c、c++、common lisp、dart、erlang、haskell、java、nodejs、php、python、perl、ruby、rust、scala 和 swift 的實作。
幾個非瀏覽器用戶端實作支援明文傳輸的 HTTP/2,h2c。最靈活的為 curl。
首先要提到的工具當然是 curl。請確定您的版本支援 HTTP/2,方法是檢查其 特性
$ curl -V curl 7.45.0 (x86_64-apple-darwin15.0.0) libcurl/7.45.0 OpenSSL/1.0.2d zlib/1.2.8 nghttp2/1.3.4 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 [...] Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2
而對於更深度的檢查,請使用 wireshark。
nghttp2 套件也包含用戶端,例如
Chrome 透過 特殊 net-internals 頁面,提供其連線中的詳細 HTTP/2 記錄。另外也有 Chrome 和 Firefox 的擴充功能,可顯示您的瀏覽器何時使用 HTTP/2。
HTTP/2 協定允許伺服器將回應推送至從未要求過的用戶端。會話的語氣為:「這裡有一個您從未發出的請求,而回應很快就會抵達...」
但有使用限制:用戶端可以停用此功能,而伺服器只能在來自用戶端的請求中進行推送。
目的是讓伺服器傳送資源給客戶端,這些資源通常是客戶端已請求的 HTML 頁面中所屬的 css 或 javascript 資源。css 所引用的圖像組等等。
對客戶端的優點是,它節省傳送請求所需的時間,從幾毫秒到半秒不等,具體取決於地球上兩者的位置。缺點是客戶端可能會收到它快取中已有的內容。當然,HTTP/2 允許提前取消此類請求,但仍然會浪費資源。
總結來說:如何最佳使用 HTTP/2 的這項功能沒有單一的好策略,而且每個人仍在嘗試中。所以,您如何使用 Apache httpd 嘗試呢?
mod_http2
檢視特定格式的 Link
標題的回應標題
Link </xxx.css>;rel=preload, </xxx.js>; rel=preload
如果連線支援 PUSH,這兩個資源將會傳送給客戶端。作為 Web 開發人員,您可以在應用程式回應或透過以下方式設定伺服器來設定這些標題
<Location /xxx.html> Header add Link "</xxx.css>;rel=preload" Header add Link "</xxx.js>;rel=preload" </Location>
如果您想使用 preload
連結而不會觸發 PUSH,您可以使用 nopush
參數,就像這樣
Link </xxx.css>;rel=preload;nopush
或者您可以使用指令完全停用伺服器的 PUSH
H2Push Off
還有更多
模組會記錄每個連線已 PUSH 的內容(基本上是 URL 的雜湊),並且不會將相同的資源 PUSH 兩次。當連線關閉時,這些資訊會被捨棄。
有人在思考客戶端如何告訴伺服器它已經有哪些內容,所以可以避免 PUSH 這些內容,但這一切都還在大量實驗中。
mod_http2
已執行的另一項實驗性質草案是 Accept-Push-Policy 標題欄位,客戶端可以定義它接受什麼樣的 PUSH 類型。
PUSH 並不總會觸發預期或期望的請求/回應/效能。網路上可以找到關於這個主題的各種研究,解釋了優缺點,以及客戶端和網路的不同特點如何影響結果。例如:單憑伺服器 PUSH 資源並不表示瀏覽器會實際使用資料。
影響被 PUSH 的回應的主要因素是模擬的請求。PUSH 的請求網址由應用程式提供,但請求標題從何而來?例如,PUSH 請求會要求 accept-language
標題嗎?如果是的話,其值是什麼?
Apache 會查看原始請求(觸發 PUSH 的請求),並將下列標題複製到 PUSH 請求:user-agent
、accept
、accept-encoding
、accept-language
、cache-control
。
忽略所有其他標頭。Cookie 也會無法複製。推送需要 Cookie 的資源將無法順利運作。這可能會引起爭議。但除非事先與瀏覽器有更明確的討論,否則讓我們謹慎行事,不要在平常不會顯示 Cookie 的地方顯示 Cookie。
推動資源的替代方法是,在回應準備妥當之前,將 連結
標頭傳送至用戶端。這是使用 HTTP 的「早期提示」功能,並且在 RFC 8297 中有說明。
如需使用此功能,您需要透過以下方式在伺服器上明確啟用:
H2EarlyHints on
(它預設未啟用,因為一些較舊的瀏覽器無法順利處理此類回應。)
如已啟用這項功能,您可以使用指令 H2PushResource
來觸發早期提示和資源推播。
<Location /xxx.html> H2PushResource /xxx.css H2PushResource /xxx.js </Location>
當伺服器開始處理請求時,會立即向用戶端傳送 "103 早期提示"
回應。這可能遠早於決定第一個回應標頭的時間,具體取決於您的網路應用程式。
如果已啟用 H2Push
,則它將會在 103 回應之後立即開始推播。但是,如果停用 H2Push
,則 103 回應仍會傳送給用戶端。