Apache运行机制
1.Apache概述
Apache是目前世界上使用最为广泛的一种Web Server,它以跨平台、高效和稳定而闻名。
Apache并不是没有缺点,它最为诟病的一点就是变得越来越重,越来越复杂,被普遍认为是重量级的 WebServer。而且Apache的server为process-based server ,也就是基于多进程的HTTP服务器,它需要对每个用户请求创建一个子进程进行响应,这样的缺点是,如果并发的请求非常多(在大型门户网站很常见),就会需要非常多的进程,从而占用极多的cpu资源和内存。因此对于并发处理不是Apache的强项。
使用另一种webServer,叫做asynchronous servers 异步服务器,比如lighttpd,nginx等等,这些WebServer的优点是运行效率很 高,但缺点也很明显,成熟度往往要低于Apache。
2.Apache组件逻辑图
Apache是基于模块化设计的,总体上看起来代码的可读性高于php的代码,它的核心代码 并不多,大多数的功能都被分散到各个模块中,各个模块在系统启动的时候按需载入。你如果想要阅读Apache的源代码,建议你直接从main.c文件读起,系统最主要的处理逻辑都包含在里面。
MPM(Multi -Processing Modules,多重处理模块)是Apache的核心组件之 一,Apache通过MPM来使用操作系统的资源,对进程和线程池进行管理。
Apache为了能够获得最好的运行性能,针对不同的平台 (Unix/Linux、Window)做了优化,为不同的平台提供了不同的MPM,用户可以根据实际情况进行选择,其中最常使用的MPM有 prefork、worker 和event三种。至于您的服务器正以哪种方式运行,取决于安装Apache过程中指定的MPM编译参数,在X系统上默认的编译参数为 prefork。由于大多数的Unix都不支持真正的线程,取决于平台特性, 选择prefork或worker 。 Windows是支持线程的平台,针对Windows平台采用Windows NT优化的多处理模块mpm_winnt。对此感兴趣的同学可以阅读有关资料,此处不再多讲。
Apache中还有一个重要的 组件就是APR(Apache portable Runtime Library),即Apache可移植运行库,它是一个对操作系统调用的抽象库,用来实现Apache内部组件对操作系统的使用,提高系统的可移植性。
Apache对于php的解析,就是通过众多Module中的php Module来完成的。
3.Apache的生命周期图
Apache的生命周期(prefork模式)示意图4. Apache的三种工作模式
Apache服务的三种工作模式:prefork、worker和event。
prefork的工作原理及配置
工作原理:
一个单独的控制进程(父进程)负责产生子进程,这些子进程用于监听请求并作出应答。Apache总是试图保持一些备用的 (spare)或是空闲的子进程用于迎接即将到来的请求。这样客户端就无需在得到服务前等候子进程的产生。在Unix系统中,父进程通常以root身份运行以便邦定80端口,而 Apache产生的子进程通常以一个低特权的用户运行。User和Group指令用于配置子进程的低特权用户。运行子进程的用户必须要对他所服务的内容有读取的权限,但是对服务内容之外的其他资源必须拥有尽可能少的权限。
这样可以减少频繁创建和销毁进程的开销,每个子进程只有一个线程,在一个时间点内,只能处理一个请求。这是一个成熟稳定,可以兼容新老模块,也不需要担心线程安全问题,但是一个进程相对占用资源,消耗大量内存,不擅长处理高并发的场景。
prefork的工作原理图
配置说明:
如何配置在Apache的配置文件httpd.conf的配置方式:
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 250
MaxConnectionsPerChild 1000
</IfModule>
- StartServers 服务器启动时建立的子进程数量,默认是5个
- MinSpareServers 空闲子进程的最小数量,默认5个;如果当前空闲子进程数少于MinSpareServers ,那么Apache将以最大每秒一个的速度产生新的子进程。此参数不要设的太大。
- MaxSpareServers 空闲子进程的最大数量,默认10;如果当前有超过MaxSpareServers数量的空闲子进程,那么父进程会杀死多余的子进程。次参数也不需要设置太大,如果你将其设置比MinSpareServers 小,Apache会自动将其修改为MinSpareServers +1的数量。
- MaxRequestWorkers 限定服务器同一时间内客户端最大接入的请求数量,默认是256;任何超过了MaxRequestWorkers限制的请求都要进入等待队列,一旦一个个连接被释放,队列中的请求才将得到服务,如果要增大这个数值,必须先增大ServerLimit。在Apache2.3.1版本之前这参数MaxRequestWorkers被称为MaxClients。
- MaxConnectionsPerChild 每个子进程在其生命周期内允许最大的请求数量,如果请求总数已经达到这个数值,子进程将会结束,如果设置为0,子进程将永远不会结束。在Apache2.3.9之前称之为MaxRequestsPerChild。
worker的工作原理及配置
工作原理:
和prefork模式相比,worker使用了多进程和多线程的混合模式,worker模式也同样会先预派生一些子进程,然后每个子进程创建一些线程,同时包括一个监听线程,每个请求过来会被分配到一个线程来服务。线程比起进程会更轻量,因为线程是通过共享父进程的内存空间,因此,内存的占用会减少一些,在高并发的场景下会比prefork有更多可用的线程,表现会更优秀一些;另外,如果一个线程出现了问题也会导致同一进程下的线程出现问题,如果是多个线程出现问题,也只是影响Apache的一部分,而不是全部。由于用到多进程多线程,需要考虑到线程的安全了,在使用keep-alive长连接的时候,某个线程会一直被占用,即使中间没有请求,需要等待到超时才会被释放(该问题在prefork模式下也存在)。
worker的工作原理图
配置说明:
配置在Apache的配置文件httpd.conf的配置方式:
<IfModule mpm_worker_module>
StartServers 3
ServerLimit 16
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 1000
</IfModule>
- StartServers 服务器启动时建立的子进程数量,在workers模式下默认是3个.
- ServerLimit系统配置的最大进程数量
- MinSpareThreads空闲子进程的最小数量,默认75
- MaxSpareThreads 空闲子进程的最大数量,默认250
- ThreadsPerChild 每个子进程产生的线程数量,默认是64
- MaxRequestWorkers /MaxClients 限定服务器同一时间内客户端最大接入的请求数量.
- MaxConnectionsPerChild 每个子进程在其生命周期内允许最大的请求数量,如果请求总数已经达到这个数值,子进程将会结束,如果设置为0,子进程将永远不会结束。在Apache2.3.9之前称之为MaxRequestsPerChild。
这里建议设置为非零,注意原因:
1)能够防止(偶然的)内存泄漏无限进行,从而耗尽内存。
2)给进程一个有限寿命,从而有助于当服务器负载减轻的时候减少活动进程的数量(重生的机会)。
Worker模式下所能同时处理的请求总数是由子进程总数乘以ThreadsPerChild 值决定的,应该大于等于MaxRequestWorkers。如果负载很大,现有的子进程数不能满足时,控制进程会派生新的子进程。默认最大的子进程总数是16,加大时 也需要显式声明ServerLimit(最大值是20000)。需要注意的是,如果显式声明了ServerLimit,那么它乘以 ThreadsPerChild的值必须大于等于MaxRequestWorkers,而且MaxRequestWorkers必须是ThreadsPerChild的整数倍,否则 Apache将会自动调节到一个相应值。
event的工作原理及配置
工作原理:
这是Apache最新的工作模式,它和worker模式很像,不同的是在于它解决了keep-alive长连接的时候占用线程资源被浪费的问题,在event工作模式中,会有一些专门的线程用来管理这些keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务器的线程,执行完毕后,又允许它释放。这增强了在高并发场景下的请求处理。
event的工作原理图
配置说明:
配置在Apache的配置文件httpd.conf的配置方式:
<IfModule mpm_event_module>
StartServers 3
ServerLimit 16
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 1000
</IfModule>
- StartServers 服务器启动时建立的子进程数量,在workers模式下默认是3个.
- ServerLimit系统配置的最大进程数量
- MinSpareThreads空闲子进程的最小数量,默认75
- MaxSpareThreads 空闲子进程的最大数量,默认250
- ThreadsPerChild 每个子进程产生的线程数量,默认是64
- MaxRequestWorkers /MaxClients 限定服务器同一时间内客户端最大接入的请求数量.
- MaxConnectionsPerChild 每个子进程在其生命周期内允许最大的请求数量,如果请求总数已经达到这个数值,子进程将会结束,如果设置为0,子进程将永远不会结束。
5.Apache的运行
Apache运行分为启动阶段和运行阶段。
5.1 启动阶段
在启动阶段,Apache主要进行配置文件解析(例如http.conf
以及Include
指令设定的配置文件等)、模块加载(例如mod_php.so,mod_perl.so
等)和系统资源初始化(例如日志文件、共享内存段等)工作。
在这个阶段,Apache为了获得系统资源最大的使用权限,将以特权用户root(X系统)或超级管理员administrator(Windows系统)完成启动。
Apache中的“php解释器”的装配过程就是在这个阶段完成的。“php解释器”就是负责解释和执行你的php代码的系统模块。这块儿会在PHP底层讲解。
把php最终集成到Apache系统中,还需要对Apache进行一些必要的设置。这里,我们就以php的mod_php5 SAPI运行模式为例进行讲解,至于SAPI这个概念后面我们还会详细讲解。
假定我们安装的版本是Apache2 和 Php5,那么需要编辑Apache的主配置文件http.conf
,在其中加入下面的几行内容:
Unix/Linux环境下:
LoadModule php5_module modules/mod_php5.so
AddType application/x-httpd-php .php
注:其中
modules/mod_php5.so
是X系统环境下mod_php5.so
文件的安装位置。
Windows环境下:
LoadModule php5_module d:/php/php5apache2.dll
AddType application/x-httpd-php .php
注:其中
d:/php/php5apache2.dll
是在Windows环境下php5apache2.dll
文件的安装位置。
这两项配置就是告诉Apache Server,通过URL收到的用户请求,凡是以php作为后缀,就需要调用php5_module模块(mod_php5.so/ php5apache2.dll
)进行处理。
这个过程可以参考以下的示意图:
Apache启动简图
注:在unix/linux下, so后缀文件是一个DSO文件,
DSO
与windows系统下的dll
是等价概念,都是把一堆函数封装在一个二进制文件中。调用它们的进程把它们装入内存后,会将其映射到自己的地址空间。
DSO全称为Dynamic Shared Object
,即动态共享对象。DLL全称为Dynamic Link Library
即动态链接库。
Apache 服务器的体系结构的最大特点,就是高度模块化。如果你为了追求处理效率,可以把这些dso
模块在apache编译的时候静态链入,这样会提高Apache 5%左右的处理性能。
5.2运行阶段
运行阶段概述
在运行阶段,Apache主要工作是处理用户的服务请求。
在这个阶段,Apache放弃特权用户级别,使用普通权限,这主要是基于安全性的考虑,防止由于代码的缺陷引起的安全漏洞。像微软的IIS就曾遭受“红色代码(Code Red)”和“尼姆达(Nimda)”等恶意代码的溢出攻击。
运行阶段流程
Apache将请求处理循环分为以下11个阶段:
Apache请求处理的11个阶段图- 1、Post-Read-Request阶段
在正常请求处理流程中,这是模块可以插入钩子的第一个阶段。对于那些想很早进入处理请求的模块来说,这个阶段可以被利用。
-
2、URI Translation阶段
Apache在本阶段的主要工作:将请求的URL映射到本地文件系统。模块可以在这阶段插入钩子,执行自己的映射逻辑。mod_alias就是利用这个阶段工作的。 -
3、Header Parsing阶段
Apache在本阶段的主要工作:检查请求的头部。由于模块可以在请求处理流程的任何一个点上执行检查请求头部的任务,因此这个钩子很少被使用。mod_setenvif就是利用这个阶段工作的。 -
4、Access Control阶段
Apache在本阶段的主要工作:根据配置文件检查是否允许访问请求的资源。Apache的标准逻辑实现了允许和拒绝指令。mod_authz_host就是利用这个阶段工作的。 -
5、Authentication阶段
Apache在本阶段的主要工作:按照配置文件设定的策略对用户进行认证,并设定用户名区域。模块可以在这阶段插入钩子,实现一个认证方法。 -
6、Authorization阶段
Apache在本阶段的主要工作:根据配置文件检查是否允许认证过的用户执行请求的操作。模块可以在这阶段插入钩子,实现一个用户权限管理的方法。 -
7、MIME Type Checking阶段
Apache在本阶段的主要工作:根据请求资源的MIME类型的相关规则,判定将要使用的内容处理函数。标准模块mod_negotiation和mod_mime实现了这个钩子。 -
8、FixUp阶段
这是一个通用的阶段,允许模块在内容生成器之前,运行任何必要的处理流程。和Post_Read_Request类似,这是一个能够捕获任何信息的钩子,也是最常使用的钩子。 -
9、Response阶段
Apache在本阶段的主要工作:生成返回客户端的内容,负责给客户端发送一个恰当的回复。这个阶段是整个处理流程的核心部分。 -
10、Logging阶段
Apache在本阶段的主要工作:在回复已经发送给客户端之后记录事务。模块可能修改或者替换Apache的标准日志记录。 -
11、CleanUp阶段
Apache在本阶段的主要工作:清理本次请求事务处理完成之后遗留的环境,比如文件、目录的处理或者Socket的关闭等等,这是Apache一次请求处理的最后一个阶段。