Linux中國

如何在 Apache 中啟用 HTTP/2

源碼

你可以從這裡得到 Apache 版本。Apache 2.4.17 及其更高版本都支持 HTTP/2。我不會再重複介紹如何構建該伺服器的指令。在很多地方有很好的指南,例如這裡

(有任何這個試驗性軟體包的相關鏈接?在 Twitter 上告訴我吧 @icing)

編譯支持 HTTP/2

在你編譯版本之前,你要進行一些配置。這裡有成千上萬的選項。和 HTTP/2 相關的是:

  • --enable-http2

啟用在 Apache 伺服器內部實現該協議的 『http2』 模塊。

  • --with-nghttp2=

指定 http2 模塊需要的 libnghttp2 模塊的非默認位置。如果 nghttp2 是在默認的位置,配置過程會自動採用。

  • --enable-nghttp2-staticlib-deps

很少用到的選項,你可能想將 nghttp2 庫靜態鏈接到伺服器里。在大部分平台上,只有在找不到共享 nghttp2 庫時才有用。

如果你想自己編譯 nghttp2,你可以到 nghttp2.org 查看文檔。最新的 Fedora 以及其它版本已經附帶了這個庫。

TLS 支持

大部分人想在瀏覽器上使用 HTTP/2, 而瀏覽器只在使用 TLS 連接(https:// 開頭的 url)時才支持 HTTP/2。你需要一些我下面介紹的配置。但首先你需要的是支持 ALPN 擴展的 TLS 庫。

ALPN 用來 協商 negotiate 伺服器和客戶端之間的協議。如果你伺服器上 TLS 庫還沒有實現 ALPN,客戶端只能通過 HTTP/1.1 通信。那麼,可以和 Apache 鏈接並支持它的是什麼庫呢?

  • OpenSSL 1.0.2 及其以後。
  • ??? (別的我也不知道了)

如果你的 OpenSSL 庫是 Linux 版本自帶的,這裡使用的版本號可能和官方 OpenSSL 版本的不同。如果不確定的話檢查一下你的 Linux 版本吧。

配置

另一個給伺服器的好建議是為 http2 模塊設置合適的日誌等級。添加下面的配置:

# 放在某個地方的這樣一行
LoadModule http2_module modules/mod_http2.so

<IfModule http2_module>
    LogLevel http2:info
</IfModule>

當你啟動伺服器的時候,你可以在錯誤日誌中看來類似的一行:

[timestamp] [http2:info] [pid XXXXX:tid numbers] 
  mod_http2 (v1.0.0, nghttp2 1.3.4), initializing...

協議

那麼,假設你已經編譯部署好了伺服器, TLS 庫也是最新的,你啟動了你的伺服器,打開了瀏覽器。。。你怎麼知道它在工作呢?

如果除此之外你沒有添加其它的伺服器配置,很可能它沒有工作。

你需要告訴伺服器在哪裡使用該協議。默認情況下,你的伺服器並沒有啟動 HTTP/2 協議。因為這樣比較安全,也許才能讓你已有的部署可以繼續工作。

你可以用新的 Protocols 指令啟用 HTTP/2 協議:

# 對於 https 伺服器
Protocols h2 http/1.1
...

# 對於 http 伺服器
Protocols h2c http/1.1

你可以給整個伺服器或者指定的 vhosts 添加這個配置。

SSL 參數

對於 TLS (SSL),HTTP/2 有一些特殊的要求。閱讀下面的「 https:// 連接」一節了解更詳細的信息。

http:// 連接 (h2c)

儘管現在還沒有瀏覽器支持,但是 HTTP/2 協議也工作在 http:// 這樣的 url 上, 而且 mod_h[ttp]2 也支持。啟用它你唯一所要做的是在 Protocols 配置中啟用它:

# 對於 http 伺服器
Protocols h2c http/1.1

這裡有一些支持 h2c 的客戶端(和客戶端庫)。我會在下面介紹:

curl

Daniel Stenberg 維護的用於訪問網路資源的命令行客戶端 curl 當然支持。如果你的系統上有 curl,有一個簡單的方法檢查它是否支持 http/2:

sh> curl -V
curl 7.43.0 (x86_64-apple-darwin15.0) libcurl/7.43.0 SecureTransport zlib/1.2.5
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp 
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets 

不好了。這些功能中沒有 'HTTP2'。你想要的是下面這樣:

sh> curl -V
url 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 pop3s rtsp smb smbs smtp smtps telnet tftp 
Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets

如果你的 curl 支持 HTTP2 功能,你可以用一些簡單的命令檢查你的伺服器:

sh> curl -v --http2 http://<yourserver>/
...
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQAAP__
> 
< HTTP/1.1 101 Switching Protocols
< Upgrade: h2c
< Connection: Upgrade
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
...
<the resource>

恭喜,如果看到了有 ...101 Switching... 的行就表示它正在工作!

有一些情況不會發生 HTTP/2 的 升級切換 Upgrade 。如果你的第一個請求有 請求數據 body ,例如你上傳一個文件時,就不會觸發升級切換。h2c 限制部分有詳細的解釋。

nghttp

nghttp2 可以一同編譯它自己的客戶端和伺服器。如果你的系統中有該客戶端,你可以簡單地通過獲取一個資源來驗證你的安裝:

sh> nghttp -uv http://<yourserver>/
[  0.001] Connected
[  0.001] HTTP Upgrade request
...
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: AAMAAABkAAQAAP__
...
[  0.005] HTTP Upgrade response
HTTP/1.1 101 Switching Protocols
Upgrade: h2c
Connection: Upgrade

[  0.006] HTTP Upgrade success
...

這和我們上面 curl 例子中看到的 Upgrade 輸出很相似。

有另外一種在命令行參數中不用 -u 參數而使用 h2c 的方法。這個參數會指示 nghttp 進行 HTTP/1 升級切換過程。但如果我們不使用呢?

sh> nghttp -v http://<yourserver>/
[  0.002] Connected
[  0.002] send SETTINGS frame 
...
[  0.002] send HEADERS frame 
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: http
...

連接馬上使用了 HTTP/2!這就是協議中所謂的 直接 direct 模式,當客戶端發送一些特殊的 24 位元組到伺服器時就會發生:

0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a

用 ASCII 表示是:

PRI * HTTP/2.0rnrnSMrnrn

支持 h2c 的伺服器在一個新的連接中看到這些信息就會馬上切換到 HTTP/2。HTTP/1.1 伺服器則認為是一個可笑的請求,響應並關閉連接。

因此,直接模式只適合於那些確定伺服器支持 HTTP/2 的客戶端。例如,當前一個升級切換過程成功了的時候。

直接模式的魅力是零開銷,它支持所有請求,即使帶有請求數據部分(查看h2c 限制)。

對於 2.4.17 版本,明文連接時默認啟用 H2Direct 。但是有一些模塊和這不兼容。因此,在下一版本中,默認會設置為off,如果你希望你的伺服器支持它,你需要設置它為:

H2Direct on

https:// 連接 (h2)

當你的 mod_h[ttp]2 可以支持 h2c 連接時,那就可以一同啟用 h2 兄弟了,現在的瀏覽器僅支持它和 https: 一同使用。

HTTP/2 標準對 https:(TLS)連接增加了一些額外的要求。上面已經提到了 ALNP 擴展。另外的一個要求是不能使用特定黑名單中的加密演算法。

儘管現在版本的 mod_h[ttp]2 不增強這些演算法(以後可能會),但大部分客戶端會這麼做。如果讓你的瀏覽器使用不恰當的演算法打開 h2 伺服器,你會看到不明確的警告 INADEQUATE_SECURITY,瀏覽器會拒接連接。

一個可行的 Apache SSL 配置類似:

SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
SSLProtocol All -SSLv2 -SSLv3
...

(是的,這確實很長。)

這裡還有一些應該調整,但不是必須調整的 SSL 配置參數:SSLSessionCacheSSLUseStapling 等,其它地方也有介紹這些。例如 Ilya Grigorik 寫的一篇超贊的博客: 高性能瀏覽器網路

curl

再次回到 shell 使用 curl(查看上面的「curl h2c」章節了解要求),你也可以通過 curl 用簡單的命令檢測你的伺服器:

sh> curl -v --http2 https://<yourserver>/
...
* ALPN, offering h2
* ALPN, offering http/1.1
...
* ALPN, server accepted to use h2
...
<the resource>

恭喜你,能正常工作啦!如果還不能,可能原因是:

  • 你的 curl 不支持 HTTP/2。查看上面的「檢測 curl」一節。
  • 你的 openssl 版本太低不支持 ALPN。
  • 不能驗證你的證書,或者不接受你的演算法配置。嘗試添加命令行選項 -k 停用 curl 中的這些檢查。如果可以工作,就重新配置你的 SSL 和證書。

nghttp

我們已經在 h2c 討論過 nghttp。如果你用它來進行 https: 連接,你會看到類似下面的信息:

sh> nghttp https://<yourserver>/
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)

這有兩種可能,你可以通過添加 -v 來檢查。如果是:

sh> nghttp -v https://<yourserver>/
[  0.034] Connected
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)

這意味著你伺服器使用的 TLS 庫沒有實現 ALPN。有時候正確安裝有點困難。多看看 Stackoverflow 吧。

你看到的也可能是:

sh> nghttp -v https://<yourserver>/
[  0.034] Connected
The negotiated protocol: http/1.1
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)

這表示 ALPN 能正常工作,但並沒有用 h2 協議。你需要像上面介紹的那樣檢查你伺服器上的 Protocols 配置。如果一開始在 vhost 部分設置不能正常工作,試著在通用部分設置它。

Firefox

更新: Apache Lounge 的 Steffen Land 告訴我 Firefox 上有個 HTTP/2 指示插件。你可以看到有多少地方用到了 h2(提示:Apache Lounge 用 h2 已經有一段時間了...)

你可以在 Firefox 瀏覽器中打開開發者工具,在那裡的網路標籤頁查看 HTTP/2 連接。當你打開了 HTTP/2 並重新刷新 html 頁面時,你會看到類似下面的東西:

在響應頭中,你可以看到奇怪的 X-Firefox-Spdy 條目中列出了 「h2」。這表示在這個 https: 連接中使用了 HTTP/2。

Google Chrome

在 Google Chrome 中,你在開發者工具中看不到 HTTP/2 指示器。相反,Chrome 用特殊的地址 chrome://net-internals/#http2 給出了相關信息。(LCTT 譯註:Chrome 已經有一個 「HTTP/2 and SPDY indicator」 可以很好的在地址欄識別 HTTP/2 連接)

如果你打開了一個伺服器的頁面,可以在 Chrome 中查看那個 net-internals 頁面,你可以看到類似下面這樣:

如果你的伺服器在上面的列表中,就表示它正在工作。

Microsoft Edge

Windows 10 中 Internet Explorer 的繼任者 Edge 也支持 HTTP/2。你也可以在開發者工具的網路標籤頁看到 HTTP/2 協議。

Safari

在 Apple 的 Safari 中,打開開發者工具,那裡有個網路標籤頁。重新載入你的伺服器上的頁面,並在開發者工具中選擇顯示了載入的那行。如果你啟用了在右邊顯示詳細視圖,看 Status 部分。那裡顯示了 HTTP/2.0 200,像這樣:

重新協商

https: 連接重新協商是指正在運行的連接中特定的 TLS 參數會發生變化。在 Apache httpd 中,你可以在 directory 配置中改變 TLS 參數。如果進來一個獲取特定位置資源的請求,配置的 TLS 參數會和當前的 TLS 參數進行對比。如果它們不相同,就會觸發重新協商。

這種最常見的情形是演算法變化和客戶端證書。你可以要求客戶訪問特定位置時需要通過驗證,或者對於特定資源,你可以使用更安全的、對 CPU 壓力更大的演算法。

但不管你的想法有多麼好,HTTP/2 中都不可以發生重新協商。在同一個連接上會有 100 多個請求,那麼重新協商該什麼時候做呢?

對於這種配置,現有的 mod_h[ttp]2 還沒有辦法。如果你有一個站點使用了 TLS 重新協商,別在上面啟用 h2!

當然,我們會在後面的版本中解決這個問題,然後你就可以安全地啟用了。

限制

非 HTTP 協議

實現除 HTTP 之外協議的模塊可能和 mod_http2 不兼容。這在其它協議要求伺服器首先發送數據時無疑會發生。

NNTP 就是這種協議的一個例子。如果你在伺服器中配置了 mod_nntp_like_ssl,那麼就不要載入 mod_http2。等待下一個版本。

h2c 限制

h2c 的實現還有一些限制,你應該注意:

在虛擬主機中拒絕 h2c

你不能對指定的虛擬主機拒絕 h2c 直連。連接建立而沒有看到請求時會觸發直連,這使得不可能預先知道 Apache 需要查找哪個虛擬主機。

有請求數據時的升級切換

對於有數據的請求,h2c 升級切換不能正常工作。那些是 PUT 和 POST 請求(用於提交和上傳)。如果你寫了一個客戶端,你可能會用一個簡單的 GET 或者 OPTIONS * 來處理那些請求以觸發升級切換。

原因從技術層面來看顯而易見,但如果你想知道:在升級切換過程中,連接處於半瘋狀態。請求按照 HTTP/1.1 的格式,而響應使用 HTTP/2 幀。如果請求有一個數據部分,伺服器在發送響應之前需要讀取整個數據。因為響應可能需要從客戶端處得到應答用於流控制及其它東西。但如果仍在發送 HTTP/1.1 請求,客戶端就仍然不能以 HTTP/2 連接。

為了使行為可預測,幾個伺服器在實現上決定不在任何帶有請求數據的請求中進行升級切換,即使請求數據很小。

302 時的升級切換

有重定向發生時,當前的 h2c 升級切換也不能工作。看起來 mod_http2 之前的重寫有可能發生。這當然不會導致斷路,但你測試這樣的站點也許會讓你迷惑。

h2 限制

這裡有一些你應該意識到的 h2 實現限制:

連接重用

HTTP/2 協議允許在特定條件下重用 TLS 連接:如果你有帶通配符的證書或者多個 AltSubject 名稱,瀏覽器可能會重用現有的連接。例如:

你有一個 a.example.org 的證書,它還有另外一個名稱 b.example.org。你在瀏覽器中打開 URL https://a.example.org/,用另一個標籤頁載入 https://b.example.org/

在重新打開一個新的連接之前,瀏覽器看到它有一個到 a.example.org 的連接並且證書對於 b.example.org 也可用。因此,它在第一個連接上面發送第二個標籤頁的請求。

這種連接重用是刻意設計的,它使得使用了 HTTP/1 切分 sharding 來提高效率的站點能夠不需要太多變化就能利用 HTTP/2。

Apache mod_h[ttp]2 還沒有完全實現這點。如果 a.example.orgb.example.org 是不同的虛擬主機, Apache 不會允許這樣的連接重用,並會告知瀏覽器狀態碼 421 Misdirected Request。瀏覽器會意識到它需要重新打開一個到 b.example.org 的連接。這仍然能工作,只是會降低一些效率。

我們期望下一次的發布中能有合適的檢查。

Münster, 12.10.2015,

Stefan Eissing, greenbytes GmbH

Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. See LICENSE for details.

該項目由 icing 維護。

via: https://icing.github.io/mod_h2/howto.html

作者:icing 譯者:ictlyh 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國