Apache的三種工作模式
作爲老牌服務器,Apache仍在不斷地發展,就目前來說,它一共有三種穩定的MPM(Multi-Processing Module,多進程處理模塊)。它們分別是 prefork、worker 和 event 。
1、Prefork MPM
關鍵字:多進程
prefork模式可以算是很古老但是非常穩定的模式。Apache在啓動之初,就預派生 fork一些子進程,然後等待請求進來,並且總是視圖保持一些備用的子進程。之所以這樣做,是爲了減少頻繁創建和銷燬進程的開銷。每個子進程中只有一個線程,在一個時間點內,只能處理一個請求。
在Unix系統中,父進程通常以root身份運行以便邦定80端口,而 Apache產生的子進程通常以一個低特權的用戶運行。User和Group指令用於配置子進程的低特權用戶。運行子進程的用戶必須要對他所服務的內容有讀取的權限,但是對服務內容之外的其他資源必須擁有儘可能少的權限。
優點:成熟,兼容所有新老模塊。進程之間完全獨立,使得它非常穩定。同時,不需要擔心線程安全的問題。(我們常用的mod_php,PHP的拓展不需要支持線程安全)
缺點:一個進程相對佔用更多的系統資源,消耗更多的內存。而且,它並不擅長處理高併發請求,在這種場景下,它會將請求放進隊列中,一直等到有可用進程,請求才會被處理。
httpd-mpm.conf 中的相關配置:
<IfModule mpm_prefork_module>
#服務器啓動時建立的子進程數量
StartServers 5
#空閒子進程的最小數量,默認5;如果當前空閒子進程數少於MinSpareServers ,那麼Apache將會產生新的子進程。此參數不要設的太大。
MinSpareServers 5
#空閒子進程的最大數量,默認10;如果當前有超過MaxSpareServers數量的空閒子進程,那麼父進程會殺死多餘的子進程。此參數也不需要設置太大,如果你將其設置比 MinSpareServers 小,Apache會自動將其修改爲MinSpareServers+1。
MaxSpareServers 10
#限定服務器同一時間內客戶端最大接入的請求數量,默認是150;任何超過了該限制的請求都要進入等待隊列,一旦一個個連接被釋放,隊列中的請求才將得到服務。
MaxClients 150
#每個子進程在其生命週期內允許最大的請求數量,如果請求總數已經達到這個數值,子進程將會結束,如果設置爲0,子進程將永遠不會結束。若該值設置爲非0值,可以防止運行PHP導致的內存泄露。
MaxRequestsPerChild 0
</IfModule>
創建的進程數,最多達到每秒32個,直到滿足MinSpareServers設置的值爲止。這就是預派生(prefork)的由來。這種模式可以不必在請求到來時再產生新的進程,從而減小了系統開銷以增加性能。
併發量請求數到達MaxClients(如256)時,而空閒進程只有10個。apache爲繼續增加創建進程。直到進程數到達256個。
當併發量高峯期過去了,併發請求數可能只有一個時,apache逐漸刪除進程,直到進程數到達MaxSpareServers爲止。
2、Worker MPM
關鍵字:多進程+多線程
worker模式比起上一個,是使用了多進程+多線程的模式。它也預先fork了幾個子進程(數量比較少),每個子進程能夠生成一些服務線程和一個監聽線程,該監聽線程監聽接入請求並將其傳遞給服務線程處理和應答。
Apache總是試圖維持一個備用(spare)或是空閒的服務線程池。這樣,客戶端無須等待新線程或新進程的建立即可得到處理。在Unix中,爲了能夠綁定80端口,父進程一般都是以root身份啓動,隨後,Apache以較低權限的用戶建立子進程和線程。User和Group指令用於配置Apache子進程的權限。雖然子進程必須對其提供的內容擁有讀權限,但應該儘可能給予他較少的特權。另外,除非使用了suexec ,否則,這些指令配置的權限將被CGI腳本所繼承。
線程比起進程會更輕量,因爲線程通常會共享父進程的內存空間,因此,內存的佔用會減少一些,在高併發的場景下,表現得比 prefork模式好。
有些人會覺得奇怪,那麼這裏爲什麼不直接使用多線程呢(即在一個進程內實現多進程),還要引入多進程?
原因主要是需要考慮穩定性,如果一個線程異常掛了,會導致父進程連同其他正常的子線程都掛了(它們都是同一個進程下的)。多進程+多線程模式中,各個進程之間都是獨立的,如果某個線程出現異常,受影響的只是Apache的一部分服務,而不是整個服務。其他進程仍然可以工作。
優點:佔據更少的內存,高併發下表現更優秀。
缺點:必須考慮線程安全的問題,因爲多個子線程是共享父進程的內存地址的。如果使用keep-alive的長連接方式,也許中間幾乎沒有請求,這時就會發生阻塞,線程被掛起,需要一直等待到超時纔會被釋放。如果過多的線程,被這樣佔據,也會導致在高併發場景下的無服務線程可用。(該問題在prefork模式下,同樣會發生)
Ps:http1.1的keep-alive的長連接方式,是爲了讓下一次的socket通信複用之前創建的連接,從而,減少連接的創建和銷燬的系統開銷。保持連接,會讓某個進程或者線程一直處於等待狀態,即使沒有數據過來。
<IfModule mpm_worker_module>
#服務器啓動時建立的子進程數量
StartServers 2
#限定服務器同一時間內客戶端最大接入的請求數量,默認是150;任何超過了該限制的請求都要進入等待隊列,一旦一個個連接被釋放,隊列中的請求才將得到服務。
MaxClients 150
#空閒子進程的最小數量
MinSpareThreads 25
#空閒子進程的最大數量
MaxSpareThreads 75
#每個子進程產生的線程數量
ThreadsPerChild 25
#每個子進程在其生命週期內允許最大的請求數量,如果請求總數已經達到這個數值,子進程將會結束,如果設置爲0,子進程將永遠不會結束。將該值設置爲非0值,可以防止運行PHP導致的內存泄露。
MaxRequestsPerChild 0
</IfModule>
理解配置:由主控制進程生成“StartServers”個子進程,每個子進程中包含固定的ThreadsPerChild線程數,各個線程獨立地處理請求。同樣,爲了儘量避免在請求到來才生成線程,MinSpareThreads和MaxSpareThreads設置了最少和最多的空閒線程數;而MaxClients設置了所有子進程中的線程總數。如果現有子進程中的線程總數不能滿足負載,控制進程將派生新的子進程。
3、Event MPM
關鍵字:多進程+多線程+epoll
這個是 Apache中最新的模式,在現在版本里的已經是穩定可用的模式。它和 worker模式很像,最大的區別在於,它解決了 keep-alive 場景下 ,長期被佔用的線程的資源浪費問題(某些線程因爲被keep-alive,掛在那裏等待,中間幾乎沒有請求過來,一直等到超時)。
event MPM中,會有一個專門的線程來管理這些 keep-alive 類型的線程,當有真實請求過來的時候,將請求傳遞給服務線程,執行完畢後,又允許它釋放。這樣,一個線程就能處理幾個請求了,實現了異步非阻塞。
event MPM在遇到某些不兼容的模塊時,會失效,將會回退到worker模式,一個工作線程處理一個請求。官方自帶的模塊,全部是支持event MPM的。
注意一點,event MPM需要Linux系統(Linux 2.6+)對Epoll的支持,才能啓用。
還有,需要補充的是HTTPS的連接(SSL),它的運行模式仍然是類似worker的方式,線程會被一直佔用,知道連接關閉。部分比較老的資料裏,說event MPM不支持SSL,那個說法是幾年前的說法,現在已經支持了。
<IfModule mpm_worker_module>
#服務器啓動時建立的子進程數量
StartServers 3
#空閒子進程的最小數量
MinSpareThreads 75
#空閒子進程的最小數量
MaxSpareThreads 250
#每個子進程產生的線程數量
ThreadsPerChild 25
#限定服務器同一時間內客戶端最大接入的請求數量,默認是150;任何超過了該限制的請求都要進入等待隊列,一旦一個個連接被釋放,隊列中的請求才將得到服務。
MaxRequestWorkers 400
#每個子進程在其生命週期內允許最大的請求數量,如果請求總數已經達到這個數值,子進程將會結束,如果設置爲0,子進程將永遠不會結束。將該值設置爲非0值,可以防止運行PHP導致的內存泄露。
MaxRequestsPerChild 0
</IfModule>