2017年11月14日 星期二

Apache HTTP Server 的多程序處理模組 (Multi-Processing Module, MPM)

在 Unix 系統,Apache HTTP Server (httpd) 2.4 提供了三種不同的多程序處理模組 (Multi-Processing Module,MPM),它們分別是:Prefork,Worker 和 Event。httpd 官網的 MPM 介紹 請參考

Prefork 模組:

Prefork 模組實作了一個非執行緒、預先分叉 (Pre-fork) 的網頁伺服器。當 httpd 在啟動時就預先 fork 一些子程序來等待請求。

優點:較為成熟穩定,兼容所有新舊模組;沒有使用執行緒,可以完全的區隔每一個不同的請求,所以不用擔心執行緒安全的問題。

缺點:每一個子程序相對的會占用更多的系統資源,消耗更多的記憶體;面對高並發 (High Concurrency) 請求時,系統效能會大幅下降。


Worker 模組:

這個模組混合使用多個子程序 (Multi-process) 和多個執行緒 (Multi-threaded)。一個單一控制程序 (父程序) 會預先啟動數個子程序,然後每個子程序會建立一些伺服器執行緒,以及一個監聽執行緒;監聽執行緒負責監聽連線 (Connection),並將連線傳遞給伺服器執行緒進行處理。相較於基於程序的伺服器,因為使用執行緒回應請求,可以有效減少系統資源的使用。

優點:使用較少的記憶體、CPU 時間等系統資源,因此相較於 Prefork 模組,較能夠回應高並發請求。

缺點:因為多個執行緒共享父程序的記憶體地址,所以必須考慮執行緒的安全問題;每個執行緒處理一個連線,會有阻塞 (Blocking) 的問題。


Event 模組:

以 Worker 模組為基礎, Event 模組一樣是使用子程序和執行緒處理客戶諯請求;最大的差別在於 Event 模組會為每個 HTTP 請求對應到一個執行緒,而 Worker 模組則是每一個 HTTP 連線(可能會有多個請求)對應到一個執行緒。在  keep-alive1 連線模式下,httpd 傳統上會持續占用整個子程序 / 執行緒來等待客戶端的資料,造成了自身的劣勢。

而在 Event 模組中,每一個程序會有一個專門的監聽執行緒來管理兩種不同的監聽插座 (Listening Socket):在 Keep-alive 狀態的所有插座,以及已經處理完畢或是只剩下等待回傳資料給客戶端的插座。因此 Event 模組可以修正 Worker 模組會碰到的 keep-alive 問題。這種架構需要利用非阻塞 (Non-blocking) 插座和現代作業系統內核的輪詢 (Polling) 功能,以避免驚群問題 (Thundering Herd Problem)。


優點:解決了在 keep-alive 情境下,資源被沒有 HTTP 請求的連線長期占用的問題。


註1. 當一個客戶端完成第一項請求時,它可以繼續維持連線,讓後續的請求使用相同的插座 (Socket),節省在建立 TCP 連線時的顯著負擔。


模組設定


下列的指令可以檢查目前的 MPM。

httpd -V


Server version: Apache/2.4.29 (Unix)
Server built:   Nov  6 2017 14:32:45
Server's Module Magic Number: 20120211:68
Server loaded:  APR 1.6.3, APR-UTIL 1.6.1
Compiled using: APR 1.6.3, APR-UTIL 1.6.1
Architecture:   64-bit
Server MPM:     prefork
  threaded:     no
    forked:     yes (variable process count)


(預設為使用 prefork 模組)


使用 Homebrew 安裝的 httpd 已經將 3 種模組都預先編譯到套件內了,只需要在 httpd.conf 啟用相關的模組就可以了。例如,要啟用 Event 模組,只需要完成下列的設定:

LoadModule mpm_event_module lib/httpd/modules/mod_mpm_event.so
#LoadModule mpm_prefork_module lib/httpd/modules/mod_mpm_prefork.so
#LoadModule mpm_worker_module lib/httpd/modules/mod_mpm_worker.so


重新啟動 httpd,就可以改為 Event 模組:

httpd -V


Server version: Apache/2.4.29 (Unix)
Server built:   Nov  8 2017 08:51:12
mac1deiMac:~ timmy$ httpd -V
Server version: Apache/2.4.29 (Unix)
Server built:   Nov  8 2017 08:51:12
Server's Module Magic Number: 20120211:68
Server loaded:  APR 1.6.3, APR-UTIL 1.6.1
Compiled using: APR 1.6.3, APR-UTIL 1.6.1
Architecture:   64-bit
Server MPM:     event
  threaded:     yes (fixed thread count)
    forked:     yes (variable process count)



預設的 MPM


根㨿 httpd 官網,在安裝 MPM 時,主要考慮下列的兩個問題

1. 系統支援執行緒嗎?

2. 系統支援執行緒安全輪詢 (thread-safe polling)嗎 (特別是 kqueue 以及 epoll 兩個函數)?

如果兩個答案都是肯定的,預設安裝的 MPM 會是 Event 模組;如果只有問題 1 的答案是肯定的,預設的 MPM 會是 Worker 模組;如果兩個答案都是否定的,預設的 MPM 會是 Prefork 模組。因為現代的作業系統都支援這兩個特性,實際上預設的 MPM 都會是 Event 模組。

不過,MPM 的選擇還牽涉到 SAPI 對於 MPM 的支援;例如,在生產環境,如果使用 PHP 的 Apache 2 Handler 模組,就不建議使用執行緒類型的 MPM。相關理由請參考

沒有留言: