Apache2 MPM 模式了解

一、MPM

  MPM(Multi-Processing Module (MPM) implements a hybrid multi-process multi-threaded server)是Apache2引入的一个概念,多路处理模块,就是将结构模块化。把核心任务处理作为一个可插拔的模块,即MPM,使其能针对不同的环境进行优化。在这个情况下,就诞生出了处理模式的概念。处理模式在Unix/Linux下现在分为 Prefork、Worker、Event 三种。

二、Prefork

  Prefork MPM基于非线程模型,和Apache 1.x版本中的处理方式很相似。Prefork MPM在所有情况下都很安全,对运行非线程安全(non-thread-safe)模式的软件如PHP,它是唯一的安全选择。对于某些应用程序,包括在 Apache 1.3上非常流行的程序(如简单静态页面、CGI脚本等),Prefork MPM是最好的选择。另一方面,prefork用单独的子进程来处理不同的请求,进程之间是彼此独立的,这也使其成为最稳定的MPM之一。但是由于每一个请求都会产生一个新的进程,导致系统资源(尤其是内存)消耗的很快,一旦并发量较大的时候,大量的Apache进程会占用巨大的内存空间。

  ServerLimit 3000 
  StartServers 750 
  MinSpareServers 5 
  MaxSpareServers 100 
  MaxClients 3000 
  MaxRequestsPerChild 10000 

  首先来看看apache各个参数的意义(引号里引用的是官方文档的描述):

  (1)ServerLimit和MaxClients 服务器最大同时响应请求数

  这个就是你当前配置的apache最大的并发响应数,对应的是apache的进程数,两个参数同时修改,MaxClients不得大于ServerLimit参数。 ServerLimit的大小,取决于你系统的资源,每个apache进程默认占用2M内存,基本可以按照这个公式来计算:最大内存*80%/2M=ServerLimit 

  (2)StartServers 750 启动时默认启动的进程数

  这个参数默认是5,因为apache会通过自动启动新进程来增加响应服务的进程数,这个值不做调整的也是可以的,会由默认的5增加到满足服务的进程数,但是会出现开始启动时的卡住。小启动参数有一个好处:就是可以让传递后后端tomcat的压力缓慢增加上来,而不是一下子增加压力。可以把这个调整到当前服务最大的并发数,当前服务最大并发连接数,可以通过监控apache进程个数:ps -ef | grep httpd | wc -l 来获得。不用调得太大,否则是无谓增加apache通过jk去跟tomcat建立的连接。
  注意:apache进程跟tomcat建立连接后,不会释放此连接,会一直保持连接,直到timeout,如果没有timeout时间,就会永久连接。timeout的设置,会在后面jk配置里说明。所以不要一次启动太多的apache进程,只启动足够用的进程即可。其他增加的流量,apache会自动调整进程数,直到MaxClients参数限定的范围。 

  (3)MinSpareServers 5 最小空闲进程

  MinSpareServers指令设置空闲子进程的最小数量。所谓空闲子进程是指没有正在处理请求的子进程。如果当前空闲子进程数少于MinSpareServers ,那么Apache将以第一秒一个,第二秒两个,第三秒四个,按指数递增个数的速度产生新的子进程。 

  (4)MaxSpareServers 10 最大空闲进程

  MaxSpareServers指令设置空闲子进程的最大数量。所谓空闲子进程是指没有正在处理请求的子进程。如果当前有超过MaxSpareServers数量的空闲子进程,那么父进程将杀死多余的子进程。可以调整这两个参数,但是这两个参数的值不能设得太大,否则apache进程太多,会导致对应开启的tomcat进程也会很多。 
  官网上关于这两个参数都有这么句话:“将此参数设的太大通常是一个坏主意。” 
  在一台压力大(并发访问2800)的服务器上,MaxSpareServers这个值设置的是200。设置了这个值的好处是不会有太多的空闲的进程在消耗资源,同时减少apache和tomcat的连接端口。关闭空闲apache进程的同时,会释放jk连接,同时释放tomcat连接数,进而减少系统资源消耗。 

  (5)MaxRequestsPerChild 10000

  MaxRequestsPerChild指令设置每个子进程在其生存期内允许伺服的最大请求数量。到达MaxRequestsPerChild的限制后,子进程将会结束。如果MaxRequestsPerChild为"0",子进程将永远不会结束。 
  将MaxRequestsPerChild设置成非零值有两个好处: 
  * 可以防止(偶然的)内存泄漏无限进行,从而耗尽内存。 
  * 给进程一个有限寿命,从而有助于当服务器负载减轻的时候减少活动进程的数量。 
  注意对于KeepAlive链接,只有第一个请求会被计数。事实上,它改变了每个子进程限制最大链接数量的行为。也就是说实际上这个时候子进程最大连接数等于MaxRequestsPerChild*MaxKeepAliveRequests。所以在开启KeepAlive后,需要同时设置MaxRequestsPerChild和MaxRequestsPerChild,确保每个apache进程在服务一定请求数后会关闭,重新开启新的子进程,避免apache进程异常导致的内存泄露和资源占用。 

  (6)Keep-Alive

  默认:ON 
  发送的请求,在MaxRequestsPerChild里面只算一个,不管这个连接发送了多少个请求。 

  (7)MaxKeepAliveRequests

  默认:100 
  一个建立好的Keep-Alive连接,允许发送的请求的个数。一旦建立连接,要么就是个数达到了断开,要么就是等KeepAliveTimeout时间到了断开连接。MaxKeepAliveRequests指令限制了当启用KeepAlive时,每个连接允许的请求数量。如果将此值设为"0",将不限制请求的数目。我们建议最好将此值设为一个比较大的值,以确保最优的服务器性能。这个数字的设置,必须考虑在一个时间段内,同一个用户访问你的服务会发多少请求。要结合KeepAliveTimeout参数来考虑。 
  举个例子,用户需要间隔时间不大于KeepAliveTimeout的时间内,连续请求10个文件,那么这个参数就应该设置成10,如果用户在连续时间里不断请求访问,则这个数值得设置得更多。否则就重新建立连接下载。一旦用户连续进行了10个请求后,并且这个用户肯定在完成这些请求后的5秒内不会再请求,甚至要在之后的很长时间后请求,那么这个KeepAliveTimeout时间就可以设置得很短,以便尽早断开这种用户,把资源让个其他用户。 

  (8)KeepAliveTimeout

  默认:5 
  "在一个建立好的Keep-Alive连接上,在MaxKeepAliveRequests个数未满的情况下,等待下一个请求的时间。" 
  如果有请求到达,那么apache等待IO响应的timeout时间时间开始生效,timeout时间没等到响应,连接被断开;如果KeepAliveTimeout时间内,没有请求到达,连接就被断开。具体设置可以参考配合MaxKeepAliveRequests参数。同时这个参数又受TimeOut参数影响,在一次成功连接中,TimeOut时间内没有等到响应,也会断开连接。 

  (9)TimeOut

  默认:300 
  "TimeOut指令用于设置Apache等待以下三种事件的时间长度: 
  1. 接受一个GET请求耗费的总时间。 
  2. POST或PUT请求时,接受两个TCP包之间的时间。 
  3. 应答时TCP包传输中两个ACK包之间的时间。 
  计时器在1.2版本之前的默认值为1200,而现在已经设置为300了,但对于绝大多数情况来说仍是足够的。没有把它默认值设的更小的原因在于代码里还有点问题:有时发送一个包之后,计时器没有复位。

  (10)工作方式:

  一个单独的控制进程(父进程)负责产生子进程,这些子进程用于监听请求并作出应答。Apache总是试图保持一些备用的 (spare)或者是空闲的子进程用于迎接即将到来的请求。这样客户端就不需要在得到服务前等候子进程的产生。在Unix中,父进程通常以root身 份运行以便邦定80端口,而 Apache产生的子进程通常以一个低特权的用户运行。User和Group指令用于设置子进程的低特权用户。运行子进程的用户必须要对它所服务的内容有 读取的权限,但是对服务内容之外的其他资源必须拥有尽可能少的权限。
  prefork的工作原理是,控制进程在最初建立“StartServers”个子进程后,为了满足MinSpareServers设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足 MinSpareServers设置的值为止。这就是预派生(prefork)的由来。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。

三、Worker

  Worker MPM支持混合的多线程多进程。由于使用线程来处理请求,所以可以处理海量请求,而系统资源的开销小于基于进程的MPM。但是,它也使用了多进程,每个进程又有多个线程,以获得基于进程的MPM的稳定性。每个进程可以拥有的线程数量是固定的。服务器会根据负载情况增加或减少进程数量。一个单独的控制进程(父进程)负责子进程的建立。每个子进程可以建立ThreadsPerChild数量的服务线程和一个监听线程,该监听线程监听接入请求并将其传递给服务线程处理和应答。

  StartServers       3        //apache已启动马上创建3个httpd进程(ps aux可以看到)
  MaxClients       2000       //同一时间最大接受2000个请求(其实就是2000个线程)
  ServerLimit       25     //apache最大能启动25个进程。
  MinSpareThreads    50     //apache至少要有50个空闲线程,用来等待接下来的请求,不满则由进程创建线程
  MaxSpareThreads     200   //apache最多能有200个线程,超出了200个线程,则杀死多余的线程
  ThreadLimit        200   //限制一个进程最多只能创建200个线程
  ThreadsPerChild     100   //设定一个进程固定创建100个线程
  MaxRequestsPerChild  10000   //设定当一个进程一共接受过10000此请求之后被杀死。以释放内存。

  从上面的配置我们可以看出,当apache启动时,启动3个httpd进程,每个进程又生成100个线程,但是超过了最大限定的数字,于是杀死100个线程,也就是一个进程。当某个时候有300个请求,目前只有200个线程,于是父进程再创建2个子进程。等请求处理完毕后,在把闲置释放到只剩下200个以内。

  Worker 由主控制进程生成“StartServers”个子进程,每个子进程中包含固定的ThreadsPerChild线程数,各个线程独立地处理请求。同样, 为了不在请求到来时再生成线程,MinSpareThreads和MaxSpareThreads设置了最少和最多的空闲线程数;而MaxClients 设置了同时连入的clients最大总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。MinSpareThreads和 MaxSpareThreads的最大缺省值分别是75和250。这两个参数对Apache的性能影响并不大,可以按照实际情况相应调节。 ThreadsPerChild是worker MPM中与性能相关最密切的指令。ThreadsPerChild的最大缺省值是64,如果负载较大,64也是不够的。这时要显式使用 ThreadLimit指令,它的最大缺省值是20000。Worker模式下所能同时处理的请求总数是由子进程总数乘以ThreadsPerChild 值决定的,应该大于等于MaxClients。如果负载很大,现有的子进程数不能满足时,控制进程会派生新的子进程。默认最大的子进程总数是16,加大时 也需要显式声明ServerLimit(最大值是20000)。需要注意的是,如果显式声明了ServerLimit,那么它乘以 ThreadsPerChild的值必须大于等于MaxClients,而且MaxClients必须是ThreadsPerChild的整数倍,否则 Apache将会自动调节到一个相应值。

四、Event

  以上两种稳定的MPM方式在非常繁忙的服务器应用下都有些不足。尽管HTTP的Keepalive方式能减少TCP连接数量和网络负载,但是 Keepalive需要和服务进程或者线程绑定,这就导致一个繁忙的服务器会耗光所有的线程。Event MPM是解决这个问题的一种新模型,它把服务进程从连接中分离出来。在服务器处理速度很快,同时具有非常高的点击率时,可用的线程数量就是关键的资源限 制,此时Event MPM方式是最有效的。一个以Worker MPM方式工作的繁忙服务器能够承受每秒好几万次的访问量(例如在大型新闻服务站点的高峰时),而Event MPM可以用来处理更高负载。

  它和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,那个说法是几年前的说法,现在已经支持了。

  # StartServers:        初始数量的服务器进程开始
  # MinSpareThreads:       最小数量的工作线程,保存备用
  # MaxSpareThreads:      最大数量的工作线程,保存备用
  # ThreadsPerChild:      固定数量的工作线程在每个服务器进程
  # MaxRequestWorkers:    最大数量的工作线程
  # MaxConnectionsPerChild:  最大连接数的一个服务器进程服务
 
五、对比

1. prefork 中没有线程的概念,是多进程模型,一个进程处理一个连接;稳定;响应快。其缺点是在连接数比较大时就非常消耗内存。

2. worker 是多进程多线程模型,一个进程有多个线程,每个线程处理一个连接。与prefork相比,worker模式更节省系统的内存资源。不过,需要注意worker模式下的Apache与php等程序模块的兼容性。

3. event 是worker模式的变种,它把服务进程从连接中分离出来,在开启KeepAlive场合下相对worker模式能够承受的了更高的并发负载。

六、优化建议

  # 升级 Apache 到最新版本,新版本往往包含性能提升和安全更新。 
  # 在 httpd.conf 中设置 "HostNameLookups off" 能避免针对每个访问者的 DNS 域名的反向查询。 
  # 对于繁忙的网站,在 httpd.conf 中设置 "MaxClients 230" 或者更高。这项设置让更多的 httpd 进程同时响应请求,并避免了处理器排队的情况发生。 
  # 采用另外一台服务器处理图片文件。 
  # 缺保您的 Web 页面和 CGI 页面采用了浏览器缓冲技术。具体的文章可以参考本站:采用 mod_gzip 加速 Zope 和 Apache 
  # 保持您的 Apache 苗条,编译那些仅仅需要的模块,在编译之前,修改 src/Configuration 文件,在那些不需要的模块之前用 # 号注释掉。 
  # 如果不需要流量日志,那么把 httpd.conf 中的 TransferLog 指向到 /dev/null/ 
  # 除非你确定使用 .htaccess 文件来控制一些目录的权限,否则设置 "AllowOverride None" ,这样就免去 Apache 在每个目录搜索 .htaccess 文件的劳役之苦。 
  # 不要让不需要的后台进程运行。 
  # 千万不要把页面或者日志文件写到网络磁盘上,例如 NFS。 
  # 不要让 Apache (httpd) 运行于 inetd 模式。 
  # 不要让 X Windows 运行在你的 Web 服务器上,用 Ctrl-Alt-Backspace 关闭 X 。 
  # 避免使用 SSI tag。
  # 在 CGI 脚本中: 
    * 文件 I/O:打开的文件数目越少越好。 
    * Shell 命令:采用全路径来调用 shell 命令。 
    * 如果你的网站主要以 CGI 来驱动,那么请使用 mod_perl。 
    * 在你的 Web 页面目录中,不要让文件数超过 1000 个,文件越多花费在定位上的时间也越多。 
  # 在 Web 服务器上的图片越少越好,保证每个图片都经由图片压缩器运行。 
  # 对你的网站做压力测试,建议采用 Apache 自带的 ab 命令来测试。 
  # 为了最好的性能,最好把网线拔掉,这样你的 Web 服务器就十分安全,而且负载马上降为 0。
上一篇:iOS 日期时间控件


下一篇:.net程序员转行做手游开发经历(三)