概述:
httpd 2.2之前(2.2也包含在內),不支持動態切換MPM。且在編譯時候,只能指定使用哪種MPM。如果,提供了 prefork、worker、event ,要改變MPM,只能在啓動httpd服務器程序的時候,指定使用:
/usr/sbin/httpd /usr/sbin/httpd.event /usr/sbin/httpd.worker
中的那一個二進制程序。
httpd 2.4就支持動態切換MPM,在配置文件中使用【LoadModule】指令就可以實現MPM的切換了。條件:我們編譯httpd時,要把:prefork、worker、event都編譯成模塊。
一、httpd服務器工作在 prefork 模型是一個進程服務響應一個用戶請求
啓動httpd服務
[root@stu13 httpd-2.4]# ./bin/apachectl start [root@stu13 httpd-2.4]# pstree | grep httpd |-httpd---5*[httpd]
查看httpd服務器工作的MPM是什麼?
[root@stu13 httpd-2.4]# ./bin/httpd -M | grep mpm mpm_prefork_module (shared)
說明:
從【pstree】命令的顯示結果,可以看到啓動httpd服務器時就啓動5個httpd服務進程用來準備接收web請求。
當然這5個進程都是由會話領導進程(該進程的所有者是root管理員)fork()而來。主進程管理子進程,如:回收子進程、創建子進程等。而響應用戶訪問請求的則是子進程。
當用戶向httpd服務器,發起訪問資源連接請求的時候,
httpd服務器會根據<IfModule mpm_prefork_module>指令定義的參數維護進程池中的進程。
當用戶向httpd服務器發起連接請求的時候,httpd的主進程就會從進程池中派一個子進程來負責響應用戶的請求,如果該服務器的併發請求量爲100的話,一下子就使用完,進程池中空閒進程數(我們定義了進程池中最小空閒進程數爲5),90個用戶的連接請求只好等待。這時主進程fork()出子進程,然後才能響應處於等待90個用戶的連接請求。雖然是一個進程負責響應一個用戶的請求,當該進程完成了用戶的請求之後,會話領導進程並不會馬上kill掉該進程。而是使用了重用的機制。意思是在進程的生命週期內它響應完負責的用戶請求後,還可以響應後續的用戶請求。這是爲了減少消耗在創建進程上面的時間。實現快速地響應用戶請求的一種機制。假如過了一會,我們的httpd服務器已經處理完這100個併發用戶的請求了。根據後續用戶的併發用戶數目有三種情況:
(1)、後續的併發用戶數小於10的話:
領導會話進程,爲了響應這100個併發請求的用戶,而fork()出的子進程,除了用來響應後面的
用戶連接請求的進程之外,其它空閒進程統統被kill掉。只保留一定數目的空閒進程。如最小空 閒進程數爲:5( MinSpareServers 5)、最大空閒進程數爲:10(MaxSpareServers 10)
(2)、後續的併發用戶數大於10而小於150的話:
響應100個併發用戶連接請求的進程都得到了重用,但由於在處理過程中進程要不斷申請和釋放 內存,次數(處理用戶的請求次數)多了就會造成一些內存垃圾,就會影響系統的穩定性且影響了
系統的有效利用,所以會話進程的生命週期是有限的,會話管理進程要kill掉一些進程。而在進
程的生命週期內,能夠處理的用戶請求數量是可以
設置的。httpd 2.2 版本和httpd 2.4版本的設置如下:
httpd 2.2 MaxRequestsPerChild 4000 httpd 2.4 MaxConnectionsPerChild 0
所以,領導進程fork()子進程是難免的。意思是說:消耗在創建進程和銷燬進程中的時間是難免的。
如果,我們的服務器性能很好的話,可以增加進程可以處理的用戶請求的數量,但是可能會影響
系統的穩定,產生一些內存垃圾。但是減少了fork()進程和kill進程的時間。在一定的程序上,
提高了響應用戶連接請求的速度。
如果,不調整進程的生命週期內可以處理多少個用戶的連接請求數的話,就要花費大量的時間
(如果我們的服務器比較繁忙的話)fork()進程,kill進程.會在一定程度上影響響應用戶請求的
速度的。
(3)、後續的併發用戶請求數大於150的話;
有可能會出現,有可能會出現連接請求超時的情況。如果我們的接收連接的緩存不夠大的話,
有些用戶的請求連接都無法接收下來。不管服務器是否能夠處理,都應該把請求接收下來再說。我們可以調大該緩存:
[root@stu13 ~]# cat /proc/sys/net/ipv4/tcp_mem 46368 61824 92736 [root@stu13 ~]# cat /proc/sys/net/ipv4/tcp_rmem 4096 87380 1978368 [root@stu13 ~]# cat /proc/sys/net/ipv4/tcp_wmem 4096 16384 1978368
當然,如果服務器的性能很好的話,我們可以調大處理的併發用戶數量,httpd 2.2 與 httpd 2.4中定義如下:
httpd 2.2 MaxClients 300 httpd 2.4 MaxRequestWorkers 150
但是,如果調大了併發用戶數(服務器的性能條件下),雖然一次性可以處理的用戶請求數增加了,但是在服務器上進程的切換的次數也增加了。因爲進程的切換是浪費CPU時間週期的。所以,我們要爲服務器定做一個黃金比例。既能處理更多的用戶併發請求,浪費在進程間切換的時間又很合理。
prefork模型的配置如下:
<IfModule mpm_prefork_module> StartServers 5 服務器啓動時就fork()出5個進程,等待用戶連接請求 MinSpareServers 5 服務器在等待客戶端連接請求的最小空閒進程數。 MaxSpareServers 10 服務器在等待客戶端連接請求的最大空閒進程數。 MaxRequestWorkers 150 允許最大的用戶併發請求數。 MaxConnectionsPerChild 0 在進程的生命週期內,它可以服務多少個請求。 </IfModule>
httpd服務器剛啓動沒有接收客戶端連接請求時候,所佔據常駐物理內存大小爲
[root@stu13 ~]./test/test.sh Date: 2014/08/10_16:30:05 user pid vsz rss comm root 19506 5252 2196 httpd daemon 19507 5252 1684 httpd daemon 19508 5252 1684 httpd daemon 19509 5252 1684 httpd daemon 19510 5252 1684 httpd daemon 19511 5252 1684 httpd --------------------total_Rss--------------------- 10616
說明:
httpd服務器所有進程佔據的常駐物理內存集約爲:10M.
2、對該httpd進行壓力測試,併發用戶數爲:100,共發起3000個請求。暫停2秒。共測試40次。
[root@stu13 httpd-2.4]# declare -i num=40 [root@stu13 httpd-2.4]# while [ "$num" -gt 0 ]; do ab -c 100 -n 3000 http://192.168.60.99/index.html; sleep 2 ; let num--; done ..... Date: 2014/08/10_16:33:02 user pid vsz rss comm root 19506 5252 2216 httpd daemon 19509 5384 2264 httpd daemon 20311 5384 2244 httpd daemon 20459 5384 2244 httpd daemon 20519 5384 2244 httpd daemon 20555 5384 2244 httpd daemon 20615 5384 2244 httpd daemon 20856 5384 2244 httpd daemon 20944 5384 2244 httpd daemon 21036 5384 2244 httpd daemon 21089 5384 2244 httpd --------------------total_Rss--------------------- 24676 Date: 2014/08/10_16:33:03 user pid vsz rss comm root 19506 5252 2216 httpd daemon 19509 5384 2264 httpd daemon 20311 5384 2244 httpd daemon 20459 5384 2244 httpd daemon 20519 5384 2244 httpd daemon 20555 5384 2244 httpd daemon 20615 5384 2244 httpd daemon 20856 5384 2244 httpd daemon 20944 5384 2244 httpd daemon 21036 5384 2244 httpd daemon 21089 5384 2244 httpd --------------------total_Rss--------------------- 24676
說明:
進行壓力測試後,httpd服務器常駐內存集大小爲:24675,約爲:24M.
prefork模式,使用一個進程服務一個用戶請求。由於進程打開的資源是獨享,如果,用戶訪問的資源相同也要打開多次的。但是,比較穩定的。
由於prefork模型的,服務用戶請求的會話進程要不斷創建、銷燬。也就是進程號PID不斷髮生變化,所以很難做到把會話進程綁定在CPU上(使用進程號做綁定)。如果我們的服務器,有多顆CUP的話,可以使用指定的CPU處理中斷請求。
prefork 工作模式,負責監聽/響應的是主進程(屬主爲root的httpd會話領導進程)是基於 select/poll 實現網絡IO利復的。這樣服務器就可以實現併發了。但由於 select/poll本身的侷限性,理論上併發能力上限爲:1024。
二、worker 工作模型是:
一個線程服務一個用戶的連接請求。線程比進程更輕量級,創建和消耗的速度比進程的創建和消耗要快得多。線程之間還可以共享資源,如:打開的文件描述符等。這樣如果:兩個客戶端請求的是同一個文件,就不需要從磁盤上加載兩次了,速度要比基於進程的響應速度快。但線程的管理要比進程複雜得多。線程之間很多資源是共享的,所以它沒有prefork模型,一個進程服務一個服務請求那麼安全穩定。況且,linux不是真線程的操作系統,worker 與 prefork 這兩種模型,在linux上的表現幾乎都一樣。通常使用prefork模型。windows 是真線程的操作系統,它對線程的支持就很好。
啓動httpd服務器
[root@stu13 httpd-2.4]# ./bin/apachectl start [root@stu13 httpd-2.4]# pstree | grep httpd |-httpd---2*[httpd---26*[{httpd}]]
查看當前httpd服務器使用的MPM是什麼?
[root@stu13 httpd-2.4]# ./bin/httpd -M | grep mpm mpm_worker_module (shared)
說明:
從【pstree】的顯示結果可以看出,httpd服務器剛起動就創建了2個子進程,且這2個子進程又創建了25個線程用來等待服務客戶端的連接請求的。剛啓動httpd服務器時(沒有接收客戶端連接請求),
worker 的模型如下:分析評估應用,可以調節下述的參數來調節httpd服務器的併發能力的。
<IfModule mpm_worker_module> StartServers 2 服務器剛啓動的時候就啓動兩個進程,這兩個進程共生 成25個線程用來等待客戶端的連接請求的。 MinSpareThreads 25 最少保持25個線程處於空閒狀態 MaxSpareThreads 75 最多保持75個純程處於空閒狀態 ThreadsPerChild 25 每個進程最多允許生成25個線程 MaxRequestWorkers 150 併發數爲150 MaxConnectionsPerChild 0 在線程的生命週期內, 線程允許服務的客戶端連接的個數。 </IfModule>
1、httpd服務器剛啓動沒有接收客戶端連接請求時候,所佔據常駐物理內存大小爲
[root@stu13 ~]./test/test.sh Date: 2014/08/10_16:40:34 user pid vsz rss comm root 23955 5428 2372 httpd daemon 23956 283160 2092 httpd daemon 23957 283160 2096 httpd --------------------total_Rss--------------------- 6560
2、對該httpd進行壓力測試,
[root@stu13 httpd-2.4]# declare -i num=40 [root@stu13 httpd-2.4]# while [ "$num" -gt 0 ]; do ab -c 100 -n 3000 http://192.168.60.99/index.html; sleep 2 ; let num--; done ..... Date: 2014/08/10_16:59:37 user pid vsz rss comm root 28920 5428 2384 httpd daemon 28921 285476 3740 httpd daemon 28922 285476 3752 httpd daemon 29110 285476 3720 httpd daemon 30774 285476 3712 httpd --------------------total_Rss--------------------- 17308 Date: 2014/08/10_16:59:38 user pid vsz rss comm root 28920 5428 2384 httpd daemon 28921 285476 3740 httpd daemon 28922 285476 3752 httpd daemon 29110 285476 3720 httpd --------------------total_Rss--------------------- 13596
說明:
因爲,線程池中定義了,每個進程最多可以生成25個線程, 假如:此時,httpd服務器共有4個進程,每個進程都產生了25個線程,4個進程都滿負荷運轉了,
但是,還是有很多用戶在等待,這時候主進程(會話領導進程),又會fork()出進程,子進程又生成線程來服務客戶端的連接請求的。進程的線程在不斷服務連接請求,會不斷申請內存釋放內存,次數多了,難免會產生內存垃圾的。影響系統的穩定。所以,httpd服務器要不斷使用新的進程替換老的進程。、
三、event:
是基於信號驅動I/O 通知機制。工作方式是:使用每線程服務N個用戶請求。event的工作模型要優於prefork和worker.
啓動httpd服務
[root@stu13 httpd-2.4]# ./bin/apachectl start [root@stu13 httpd-2.4]# pstree | grep httpd |-httpd---2*[httpd---26*[{httpd}]]
1、查看 httpd服務器的工用模型
[root@stu13 httpd-2.4]# ./bin/httpd -M | grep mpm mpm_event_module (shared)
httpd服務器工作在event模式下線程池的控制。也worker的線程池很相似,但event的工作方式是:一個線程服務N個用戶請求。
<IfModule mpm_event_module> StartServers 2 服務器啓動的時候就生成2個子進程, 該子進程共生成25個線程等待客戶端的連接請求 MinSpareThreads 25 線程池中保持最少25個空閒線程。 MaxSpareThreads 75 線程池中保持最多75個空閒線程。 ThreadsPerChild 25 每個進程最多可以生成25個線程。 MaxRequestWorkers 150 最大併發用戶數爲:150 MaxConnectionsPerChild 0 線程能夠服務的用戶請求數據。 0 表示不做限定。 </IfModule>
httpd服務器沒有服務用戶請求時,所佔據常駐物理集大小爲
[root@stu13 ~]# test/test.sh Date: 2014/08/10_17:04:58 user pid vsz rss comm root 30948 5436 2352 httpd daemon 30949 283168 2084 httpd daemon 30950 283168 2088 httpd --------------------total_Rss--------------------- 6524
2、對該httpd進行壓力測試,
[root@stu13 httpd-2.4]# declare -i num=40 [root@stu13 httpd-2.4]# while [ "$num" -gt 0 ]; do ab -c 100 -n 3000 http://192.168.60.99/index.html; sleep 2 ; let num--; done ..... Date: 2014/08/10_17:08:26 user pid vsz rss comm root 30948 5436 2352 httpd daemon 30949 287172 6076 httpd daemon 30950 288372 6704 httpd --------------------total_Rss--------------------- 15132 Date: 2014/08/10_17:08:27 user pid vsz rss comm root 30948 5436 2352 httpd daemon 30949 287172 6076 httpd daemon 30950 288372 6704 httpd --------------------total_Rss--------------------- 15132
說明:
通過併發測試後,進程的進程號並沒有改變。則工作在 worker模型下的,壓力測試後,進程號已經發生了改變。
event 和 worker的線程池的配置都一樣,由於 event 是基於信號驅動I/O 通知機制,每個進程可以服務N個用戶請求。
而worker是一個線程服務一個請求。在請求沒有完成之前,該線程是與它服務的請求綁定的。
worker需要大量的創建進程生成線程,銷燬線程,殺死進程的過程。而event則不需要頻繁的創建銷燬。