httpd 服務器的三大引擎 prefork、worker、event分析

概述:

     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則不需要頻繁的創建銷燬。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章