概述:

     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则不需要频繁的创建销毁。