Apache HTTP Server 版本 2.4
描述 | worker MPM 的變型,其目標只對具有活動處理作業的連線使用執行緒 |
---|---|
狀態 | MPM |
模組識別項 | mpm_event_module |
來源檔案 | event.c |
event
多重處理模組 (MPM) 設計用於透過將一些處理工作交由監聽器執行緒來允許同時處理更多要求,並釋放工作執行緒來處理新的要求。
如要使用 event
MPM,在建置 httpd
時,將 --with-mpm=event
新增至 configure
指令碼的參數。
event
基於 worker
MPM,實作混合多重程序多執行緒伺服器。單一路由程序 (父程序) 負責啟動子程序。每個子程序建立 ThreadsPerChild
指令中指定的固定伺服器執行緒數目,同時建立一個監聽程序執行緒,用於監聽連線,並在連線抵達時傳遞給工作執行緒處理。
執行時期設定指令與 worker
提供的指令相同,僅新增 AsyncRequestWorkerFactor
。
此 MPM 嘗試修正 HTTP 中的「保持存活問題」。當客戶端完成第一項要求後,其可以維持連線開啟,透過同一 socket 傳送進一步的要求,並在建立 TCP 連線時節省大量的額外負擔。然而,Apache HTTP 伺服器傳統上會讓整個子程序或執行緒持續等待來自客戶端的資料,這也帶來自身的缺點。為了解決這個問題,此 MPM 使用一個專屬的監聽執行緒執行每個程序,用於處理監聽 socket,所有處於保持存活狀態的 socket,已由處理常式和通訊協定過濾器完成其工作,以及僅剩將資料傳送給客戶端的 socket。
這個新架構善用非阻擋 socket 和 APR 揭露的現代核心功能(如 Linux 的 epoll),不再需要設定mpm-accept
Mutex
來避免驚群現象。
單一程序/執行緒區塊可以處理的總連線數由 AsyncRequestWorkerFactor
指令調整。
非同步連線需要一個固定的專屬工作執行緒和先前的 MPM,但 event 不需要。mod_status
的狀態頁面會在非同步連線區段中顯示新的欄位
write()
至套接字會傳回EWOULDBLOCK
或EAGAIN
在閒置一段時間後才能再次寫入。持有套接字的工作執行緒或許能夠將等候中的任務卸載至偵聽執行緒,而偵聽執行緒將在套接字提出事件後(例如,「套接字現在可寫入」)將其重新指派至第一個可用的閒置工作執行緒。請檢查限制區段以取得更多資訊。KeepAliveTimeout
發生,那麼套接字將由偵聽器關閉。如此一來,工作執行緒不必負責閒置套接字,而可以重新用於處理其他要求。這些改善對 HTTP/HTTPS 連線皆有效。
此 MPM 早期顯示出某些可擴充性瓶頸,導致出現下列錯誤:「計分板已滿,未達 MaxRequestWorkers 值」。MaxRequestWorkers
會限制在任何特定時間點提供的同時請求數,以及允許的程序數 (MaxRequestWorkers
/ ThreadsPerChild
);同時,計分板會顯示所有執行中程序以及它們的工作執行緒狀態。如果計分板已滿 (所以所有執行緒皆為非閒置狀態),但提供的活動請求數未達 MaxRequestWorkers
,表示其中某些執行緒會封鎖本可提供但反而是排隊等候的新請求 (其上限由 ListenBacklog
設定)。大部分時候,執行緒會停留在 Graceful 狀態,表示它們正在等候完成與 TCP 連線相關的工作,以安全地終止並釋出計分板插槽 (例如,處理長時間執行之請求、速度很慢的客戶端,或已啟用保持連線功能的連線)。最常見的是發生下列兩個情況
MaxSpareThreads
)。這特別容易發生問題,因為當負載再次增加時,httpd 會嘗試啟動新程序。如果此模式會不斷重複,程序數可能會增加不少,最終變成一堆舊程序嘗試停止,而新程序則嘗試執行某些工作。從 2.4.24 開始,mpm-event 變的更聰明,能夠以更好的方式處理優雅終止。部分改進如下
ServerLimit
。MaxRequestWorkers
和 ThreadsPerChild
會用於限制活動程序的數量;同時,ServerLimit
也會考慮正在執行優雅關閉的程序,以在需要時允許有額外的插槽。其構想是使用 ServerLimit
指示 httpd 在影響系統資源之前,整體可以承受多少程序。上一個重點中所述的行為可透過 mod_status
中連線摘要表格的兩個新欄位「スロット」和「正在中斷」完整觀察到。前者指示 PID,而後者指示處理程序是否正在中斷;額外的狀態「是 (舊世代)」指示處理程序在順利重新啟動後仍在執行。
改良過的連線處理可能不適用於已宣稱與事件不相容的特定連線篩選器。在這些情況中,此 MPM 會回復為 worker
MPM 的行為,並為每個連線保留一個工作執行緒。與伺服器一起配送的所有模組與事件 MPM 相容。
目前的類似限制用於包含輸出篩選器的要求,而輸出篩選器需要讀取和/或修改整個回應本體。如果當篩選器正在處理資料時伺服器端連線遭到封鎖,且篩選器產生的資料量過大而無法緩衝在記憶體中,則用於該要求的執行緒不會在 httpd 等待將待處理資料傳送至伺服器端時釋放。
舉例來說,我們可以考慮以下兩個情況:提供靜態資產(例如 CSS 檔案)相較於提供從 FCGI/CGI 或代理伺服器擷取的內容。前者可以預測,換言之,事件 MPM 對內容結束具有完全的可視性,且可以使用事件:提供回應內容的工作執行緒可以在傳回 EWOULDBLOCK
或 EAGAIN
之前清除第一個位元組,委派其餘部份給監聽器。監聽器依序等待 socket 上的事件,並委派工作將內容的其餘部份清除至第一個閒置工作執行緒。同時,在後一個範例(FCGI/CGI/代理內容)中,MPM 無法預測回應結束,且工作執行緒必須在將控制權回傳給監聽器之前完成其工作。唯一的替代方法是將回應緩衝在記憶體中,但這並非為顧及伺服器穩定性和記憶體使用量而能採行的最安全選項。
事件模型得以實現是由於新的 API 導入到所支援作業系統中
在這些新 API 可用之前,必須使用傳統的 select
和 poll
API。如果用於處理許多連線或是連線的速率很高,這些 API 會變慢。新的 API 能夠監視更多的連線,並且當要監視的連線組經常變更時,它們的執行效能會好很多。因此,這些 API 能夠寫出事件 MPM,這種 MPM 比較容易擴充到有許多閒置連線的典型 HTTP 模式。
MPM 假設底層的 apr_pollset
實作足夠安全。這能讓 MPM 避免過度的高層鎖定,或是為了寄送保持連線 Socket 而喚醒偵聽器執行緒。目前這只與 KQueue 和 EPoll 相容。
此 MPM 依賴於 APR 的原子比較及交換運算執行緒同步。如果您要編譯 x86 目標而且不需要支援 386,或是您要編譯 SPARC 而且不需要在 UltraSPARC 之前的晶片上執行,請將 --enable-nonportable-atomics=yes
加入 configure
指令碼的引數。這將導致 APR 使用舊有 CPU 中不存在的有效運算碼來實作原子運算。
此 MPM 在缺乏良好執行緒的舊平台上無法良好執行,但因為 EPoll 或 KQueue 的需求,這一點沒有異議。
libkse
,還是有可能在 FreeBSD 5.2.1 上執行此 MPM (請參閱 man libmap.conf
)。glibc
版本已編譯並支援 EPoll。描述 | 限制每個處理程序的同時連線 |
---|---|
語法 | AsyncRequestWorkerFactor 係數 |
預設 | 2 |
上下文 | 伺服器組態 |
狀態 | MPM |
模組 | 事件 |
相容性 | 適用於版本 2.3.13 及更新版本 |
事件 MPM 以非同步方式處理部分連線,其中請求工作執行緒僅會在需要時短時間分配,而其他連線則會保留每個連線一個請求工作執行緒。這可能導致所有工作執行緒都忙碌,而且沒有工作執行緒有空處理已建立的非同步連線上的新工作。
為了緩解此問題,事件 MPM 會執行兩件事
此指令可用於微調處理程序連線限制。如果目前連線數目(不計算「關閉」狀態的連線)小於以下值,**程序**會接受新連線:
ThreadsPerChild
+ (AsyncRequestWorkerFactor
* 閒置工作程序數)
可根據閒置工作程序執行緒的平均值,計算所有程序的最大同時連線數,公式如下:
(ThreadsPerChild
+ (AsyncRequestWorkerFactor
* 閒置工作程序數)) * ServerLimit
ThreadsPerChild = 10 ServerLimit = 4 AsyncRequestWorkerFactor = 2 MaxRequestWorkers = 40 idle_workers = 4 (average for all the processes to keep it simple) max_connections = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit = (10 + (2 * 4)) * 4 = 72
當所有工作程序執行緒都處於閒置狀態時,可用更簡單的方式計算同時連線數的最大絕對值:
(AsyncRequestWorkerFactor
+ 1) * MaxRequestWorkers
ThreadsPerChild = 10 ServerLimit = 4 MaxRequestWorkers = 40 AsyncRequestWorkerFactor = 2
如果所有程序的所有執行緒都處於閒置狀態,則:
idle_workers = 10
我們可用兩種方式計算同時連線數的最大絕對值:
max_connections = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit = (10 + (2 * 10)) * 4 = 120 max_connections = (AsyncRequestWorkerFactor + 1) * MaxRequestWorkers = (2 + 1) * 40 = 120
調整 AsyncRequestWorkerFactor
需要瞭解 httpd 在每個特定案例中處理的流量,因此變更預設值需要透過 mod_status
進行廣泛的測試和資料收集。
在版本 2.3.13 之前,MaxRequestWorkers
稱為 MaxClients
。以上的變數顯示,對於事件 MPM 而言,舊名稱無法精確描述其意義。
AsyncRequestWorkerFactor
可使用非整數參數,例如「1.5」。