一個 HTTP/2 的故事

2019-01-01 16:57:00

大約一個月前,有人在我所在的 IRC 頻道中提到了 HTTP/2。由於某種原因,我從未聽說過它,而且新協定的一些功能(比如無需開啟多個 TCP 連線就能復用請求)似乎很酷。

說實話,我剛剛重寫了管理我們備份程式的 Puppet 程式碼,啟用 HTTP/2 似乎是一種轉向另一個大型專案之前有效的拖延方式。這有多難?

結果我花了大約 25 個小時來完成。坐下來穿上舒適的拖鞋,聽聽這個 HTTP/2 的故事!

被詛咒的 HTTP/1.1

當我第一次看到如何在 Apache 上啟用 HTTP/2 時,這似乎是一項非常簡單的任務。文件提到載入 http2 模組並通過如下組態檔確保新協定優先:

Protocols h2 h2c http/1.1H2Push          onH2PushPriority  *                       afterH2PushPriority  text/css                beforeH2PushPriority  image/jpeg              after   32H2PushPriority  image/png               after   32H2PushPriority  application/javascript  interleaved

這當然很容易。即使 Apache 中的所有內容都已正確設定,網站仍然使用的是 HTTP/1.1。不過,顯然我做得沒錯,因為我的網站現在傳送了一個新的 HTTP 頭:Upgrade: h2, h2c

在浪費了大量時間偵錯 TLS 金鑰(HTTP/2 與 TLS 1.1 不相容)之後,我終於發現問題是沒有使用正確的 Apache 多進程處理模組。

事實證明,在使用 mpm_prefork(預設 MPM)時,Apache 不會使用 HTTP/2,因為 mod_http2 不支援它。儘管 Apache 還有兩個其他的 MPM,但只有 mpm_prefork 支援 mod_php。突然之間,新增對 HTTP/2 的支援意味著我們將要把所有的 PHP 網站切換到 PHP-FPM。

掉進兔子洞

在很長一段時間裡,一位好友一直試圖讓我相信 PHP-FPM 的優點。雖然表面上看起來很好,但我從來沒有真正試過。它看起來很複雜。常規的 mod_php 做得很好,還有其他事情需要我注意。

事實上,這次的 HTTP/2 事件是讓我鑽研它的一個契機。在我理解了 FPM 池的工作原理後,它實際上很容易設定。由於我不得不重寫我們用於部署網站的 Puppet 組態檔,我也藉此機會鞏固了一堆東西。

PHP-FPM 允許你在不同的 Unix 使用者下執行網站來增加隔離性。最重要的是,我決定是時候讓我們伺服器上的 PHP 程式碼以唯讀模式執行,並且不得不為我們的 Wordpress、Nextcloud、KanBoard 和 Drupal 範例調整一些東西來減少報錯。

過了很長時間通過 Puppet 的自動任務後,我終於能夠全部關閉 mod_phpmpm_prefork 並啟用 mpm_eventmod_http2。PHP-FPM 和 HTTP/2 帶來的速度提升不錯,但更讓我感到高興的是這次磨練增強了我們的 Apache 處理 PHP 的能力。