Apache HTTP 服务器被设计为一个功能强大,并且灵活的 web 服务器, 可以在很多平台与环境中工作。不同平台和不同的环境往往需要不同 的特性,或可能以不同的方式实现相同的特性最有效率。Apache 通过模块化的设计来适应各种环境。这种设计允许网站管理员通过在 编译时或运行时,选择哪些模块将会加载在服务器中,来选择服务器特性。
Apache 2.0 扩展此模块化设计到最基本的 web 服务器功能。 它提供了可以选择的多处理模块(MPM),用来绑定到网络端口上,接受请求, 以及调度子进程处理请求。
MPM的设计,带来两个重要的好处:
Apache 能更优雅,更高效率的支持不同的平台。
Apache 能更好的为有特殊要求的站点定制。例如,要求更高伸缩性的站点可以选择使用线程的 MPM,即 worker 或 event;需要可靠性或者与旧软件兼容的站点可以使用 prefork。
在用户看来,MPM 很像其它 Apache 模块。主要是区别是,在任何时间, 必须有一个,而且只有一个 MPM 加载到服务器中。
MPM 必须在编译前,./configure 配置时指定,然后编译到服务器程序中。 仅当编译器知道使用了线程时,它才有能力优化许多函数。
为了使用指定的 MPM,请在执行 configure
时,使用参数 --with-mpm=NAME。NAME 是指定的 MPM 名称,我们只可以指定其中一个MPM模块。编译完成后,可以使用 ./httpd -l
来确定选择的 MPM。 此命令会列出编译到服务器程序中的所有模块,包括 MPM。 Unix/Linux支持三种MPM, Prefork, worker, event,其中event模型在apache2.2版本是测试版,不建议在生产环境中使用,到apache2.4,event模型是稳定版。
prefork 多进程模型
预先生成进程,一个请求用一个进程处理。优点:稳定可靠,任何一个进程崩溃都不会影响其他进程;缺点:性能差,特别是并发量非常大时,非常消耗内存资源,会有大量的进程切换。
worker 多线程模型
基于线程,启动多个进程,每个进程中生成多个线程。进程负责响应请求,而线程负责处理请求。因为linux不是原生支持线程,线程同步模型复杂,必须处理锁争用的问题,所以在Linux上不建议。测试发现,在linux系统上,worker 的效率并没有 prefork 高。这是默认不是worker的原因。
event IO多路复用,基于事件驱动的模型。一个进程处理多个套接字,一个进程同时处理多个请求
可以使用 httpd -l
来确定当前选择的 MPM。 此命令会列出编译到服务器程序(modules compiled into the server)中的所有模块,包括 MPM。
# -l Output a list of moudles compiled into the server. This will not list dynamically l# oaded modules included using the LoadModule directive. [root@localhost ~]# httpd -l Compiled in modules: core.c # 核心 prefork.c # 编译到httpd服务器的MPM模块 http_core.c mod_so.c # 支持DSO动态模块加载
prefork 为多路模块MPM 实现了一个进程模型,非线程型的,预派生 的web服务器。prefork 适用于没有线程安全库,需要避免线程兼容性问题的系统。它是要求将每个请求相互独立的情况下最好的MPM,这样一个请求出现问题,不至于影响其他的请求。
prefork模式使用多个子进程,每个子进程在某个确定的时间只能维持一个连接。在大多数平台上,Prefork MPM在效率上要比Worker MPM 要高,但是内存使用大的多。prefork在Linux下工作,比worker更有优势。
一个单独的控制进程(父进程)负责生产子进程,这些子进程用于监听请求并作出应答。apache 总是试图保持一个备用的(spare)或是空闲的子进程用于迎接即将到来的请求。这样客户端就无需在得到服务前等候子进程的产生。在unix系统中,父进程通常是以root 身份运行以便绑定80端口,而apache生产的子进程通常一个低特权的用户运行。User和Group指令用于配置配置子进程的低特权用户。运行子进程的用户必须要对他所服务的内容用读取的权限,但是对服务内容之外的其他资源必须拥有尽可能少的权限。
当httpd服务启动之后,会启动一个主进程(master process),它负责监听用户请求,以及派生和销毁子进程。它并不处理用户请求,一旦请求来了之后,调度其子进程(work process)来响应,然后继续监听其他请求。
需要考虑的是:刚启动的时候,创建多少空闲进程呢?为了快速响应用户请求,会创建几个空闲进程,当请求来了,可以快速的响应。如果请求太多,那么则会派生更多的进程来响应请求,但是假设到了晚上,访问量不大时,我们应该把多余的空闲进程回收,以免占用资源。
master process的主要作用:
1、启动服务时绑定特权端口
2、派发和回收子进程
3、读取分析主配置文件
4、监听用户请求并且调度子进程来响应
配置:编辑主配置文件 /etc/httpd/conf/httpd.conf
# prefork MPM <IfModule prefork.c> # 判断是否有prefork.c这个模块,有就生效,否则无效 StartServers 8 # 服务初始化的工作进程数(work process) MinSpareServers 5 # 保持的最少空闲进程数 MaxSpareServers 20 # 保持的最大空闲进程数 ServerLimit 256 # 保持的最大活动进程数,设定MaxClients的上限值 MaxClients 256 # 最大并发连接数 MaxRequestsPerChild 4000 # 每个子进程在生命周期能服务的最大请求数 </IfModule>
IfModule 指令,意为判断模块是否存在,如果存在那么参数则生效,反之如果不存在此模块,参数将不会生效。
StartServer 8
指定服务器启动是建立的子进程的数量,prefork 默认为5。为了满足MinSpareServers设置的需要创建一个进程, 等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足 MinSpareServers设置的值为止。这就是预派生 (prefork)的由来。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。
MinSpareServers 5
指定维持空闲子进程的最小数量,默认为5。假如当前空闲子进程数少于MinSpareServers,那么apache 将以每秒一个的速度产生新的子进程。此参数不要设置的太大。举个例子:MinSpareServers 限定为5个,当有4个请求来了之后, 8 - 4 还剩4个,那么为了维持5个MinSpareServers, 就必须再创建一个子进程。
MaxSpareServers 20
设置保持空闲进程的最大数量,默认为20。如果当前有超过MaxSpareServers数量的空闲子进程,那么父进程将杀死(kill)多余的子进程。此参数不要设置过大。如果该参数设置比MinSpareServers小,apache将会自动将其修改成"MinSpareServers+1”。如果站点负载较大,可考虑同时加大MinSpareServers和MaxSpareServers。
MaxClients 256
限定同一时间客户端最大接入的数量(单个进程并发线程数)。默认为256,任何超过MaxClients限制的请求都会进入等候队列,一旦一个链接被释放,队列中的请求将得到服务。MaxClients的值不能超过 ServerLimit, 如果要增大这个值,必须同时增加ServerLimit.理论上来说这个值越大,性能就越好。最重要的值是MaxClients允许足够多的工作进程,同时又不会导致服务器当机。
可通过查看httpd进程数:# ps -ef | grep httpd | wc -l,选择高峰期,结合当时服务器资源使用情况,来设置MaxClients。
ServerLimit 256
默认的MaxClient最大是256个进程,假如想设置更大的值,就需要加上ServerLimit这个数。 200000是ServerLimit这个参数的最大值。假如需要更大,则必须编译apache,此前都是无需重新编译apache。生效前提是,必须放在其他指令的前面。
要调整ServerLimit的值,首先必须关闭httpd服务,调整后再重启httpd。
MaxRequestsPerChild
每个子进程在其生存期内允许伺服的最大请求数量(每一个子进程最多响应多少次请求)。默认为10000到达MaxRequestsPerChild的限制后,子进程将会结束。
如果MaxRequestsPerChild 为0,子进程将永远不会结束。将MaxRequestsPerChild 设置成非零值有两个好处:
可以防止(偶然的)内存泄漏无限进行,从而耗尽内存。
给进程一个有限寿命,从而有助于当服务器负载减轻的时候减少活动进程的数量。
对于KeepAlive链接,只有第一个请求会被计数。事实上,它改变了每个子进程限制最大链接数量的行为。
我们看看httpd进程:
[root@localhost ~]# ps -ef | grep httpd root 10692 1 0 20:17 ? 00:00:00 /usr/sbin/httpd # master process apache 10786 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd apache 10787 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd apache 10788 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd apache 10789 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd apache 10790 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd apache 10791 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd apache 10792 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd apache 10793 10692 0 21:39 ? 00:00:00 /usr/sbin/httpd
Prefork是一个进程处理一个请求,在启动httpd的时候,如果选择的是这种模式,会首先创建一个主进程(控制进程)和StartServers个子进程,可以看到StartServers为8个.由于进程和进程之间是独享内存的,所以一个进程崩溃了不会影响到其他的进程,所以Prefork模式的稳定性比较好,但是进程多了消耗的内存会比较大.
主进程在创建了StartServers个子进程后,为了满足MinSpareServer的设置需求,会先创建一个进程,等待一秒,创建二个进程,再等待一秒,创建四个进程....以几何数增加创建的进程,最多达到每秒创建32个,直到满足MinSpareServer的设置(可以看到以下的MinSpareServer为5),这就是预派生(Prefork)的由来,这样不必等到有请求到来时才花时间创建新的进程,提高了系统响应速度以增加性能.
worker MPM 使用多个子进程,每个子进程有多个线程。每个线程在每个确定的时间只能维持一个连接。通常来说,在一个高流量的HTTP服务器上,Worker MPM 是个比较好的选择,因为Worker MPM 的内存使用比Prefork MPM 要低的多。但是worker MPM 也有缺陷。如果一个线程崩溃,整个进程就会连同其任何线程一起“死掉”,由于线程共享内存空间,所以一个程序在运行时必须被系统识别为 “每个线程都是安全的”。
当第一个用户请求到达的时候线程发现需要复制文件,通过内核将文件复制给其进程,所以第二个用户请求同一个文件,对于第二个线程来说这个文件已存在,因为是公用同一个空间,所以速度得到提升;
其缺点:线程很有可能产生资源争用,并不是全状态并行。所以一个进程里不能启动太多线程,所以可启动多个进程从其启动的进程再启多个线程,但无论如何每个线程也都是一个独立的执行实体(执行必须给cpu资源)
更改apache的MPM为woker模式
我们是通过yum安装的httpd,它默认已经编译了prefork, worker两个MPM,所以我们可以切换。如果是手动编译的话, 我们仅能指定一个 --with-mpm=NAME。
/etc/init.d/httpd 是 httpd 服务的启动脚本,此脚本的配置文件为: /etc/sysconfig/httpd, 那么我们可以编辑配置文件/etc/sysconfig/httpd:
# vi /etc/sysconfig/httd # 把下面这一行的注释去掉 HTTPD=/usr/sbin/httpd.worker # 切换模式必须重启服务 # service httpd restart
worker默认的配置:
<IfModule worker.c> 如果有这个模块就启用 StartServers 4 # 初始化的子进程数 MaxClients 300 # 并发请求最大数 MinSpareThreads 25 # 最小空闲线程数total=所有进程的线程数加起来 MaxSpareThreads 75 # 最大空闲线程数 total ThreadsPerChild 25 # 每个子进程可生成的线程数 MaxRequestsPerChild 0 # 每个子进程可服务的最大请求数,0表示不限制
[root@localhost ~]# ps -ef | grep httpd root 2395 1 0 15:36 ? 00:00:00 /usr/sbin/httpd.worker apache 2397 2395 0 15:36 ? 00:00:00 /usr/sbin/httpd.worker apache 2398 2395 0 15:36 ? 00:00:00 /usr/sbin/httpd.worker apache 2399 2395 0 15:36 ? 00:00:00 /usr/sbin/httpd.worker
worker的工作原理是,由主控制进程生成“StartServers”个子进程,每个子进程中包含固定的ThreadsPerChild线程数,每个线程处理一个请求,线程是共享内存空间的,所以一个线程崩溃会导致在这个进程下的所有线程都崩溃,所以他的稳定性没有Prefork好,但是内存使用率比Prefork低。同样,为了不在请求到来时再生成线程,MinSpareThreads和MaxSpareThreads设置了最少和最多的空闲线程数;而MaxClients设置了所有子进程中的线程总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。
参数:
�CStartServer:服务器启动时一次性建立的子进程数量
�CMaxClients:设定apache同一时间处理的最大请求数量,这里是指最大线程数量,大小仍受ServerLimit限制且必须是ThreadsPerChild的倍数。
�CMinSpareThreads:设置最少的空闲线程数
�CMaxSpareServers:设置最大的空闲线程数,必须大于等于MinSpareThreads加上ThreadsPerChild的和(因为每个子进程拥有的线程数是固定的)
�CThreadsPerChild:每个子进程建立的常驻线程,之后不会再创建新的线程。大小受ThreadLimit限制。
�CMaxRequestsPerChild:每个子进程最多处理的请求数量,同prefork的一样
�CServerLimit:服务器允许配置的进程数上限,和ThreadLimit共同限制MaxClients(MaxClients <= ServerLimit *ThreadsPerChild)。
�CThreadLimit:TreadsPerChild可以配置的线程数上限,默认为64。
根据网上一些测试数据,实际情况下还是选prefork工作模式较好。
MinSpareThreads和MaxSpareThreads的最大缺省值分别是25和75。这两个参数对Apache的性能影响并不大,可以按照实际情况相应调节。MinSpareThreads,MaxSpareThreads 是总体的,所有进程所派生出来的线程数总数加起来
ThreadsPerChild是worker 中影响性能最重要的一个指标,Worker模式下所能同时处理的请求总数是由StartServers总数乘以ThreadsPerChild的值决定的,应该大于等于MaxClients
总结:
这些参数,如何设置才更合理呢? 没有绝对的说法,我们必须根据实际需求进行配置,通过压力测试来调整,根据实际web服务器的压力进行动态调整,以取得一个良好的折中。
压力测试工具:
LoadRunner
ab ab -c 100 -a 10000 http://localhost/index.html
参考:
http://blog.chinaunix.net/uid-20441206-id-3360689.html
http://blog.chinaunix.net/uid-17238776-id-4353851.html
http://www.cnblogs.com/ghj1976/archive/2011/01/11/1932764.html