首页 > 编程笔记 > Linux笔记 阅读:13

Apache的3种工作模式(prefork、worker和event)

Apache HTTP 服务器被设计为一个强大的、灵活的、能够在多种平台以及不同环境下工作的服务器。不同的平台和不同的环境经常产生不同的需求,而 Apache 凭借它的模块化设计很好地适应了各种不同环境。这一设计使得 Linux 运维工程师能够在编译和运行时通过载入不同的模块来使得服务器拥有不同的附加功能。

在 Apache2.0 的版本中又将这种模块化的设计延伸到了 Web 服务器的基础功能上。这个版本带有多路处理模块(MPM)的选择,用来处理网络端口绑定、接受客户端请求并指派子进程来处理这些请求。

将模块化设计延伸到 Web 服务器的基础功能上主要有两点好处:
从用户角度来看,多路处理模块(MPM)更像是 Apache 的一个模块。主要的不同点在于:不论何时,必须有且仅有一个 MPM 被载入到服务器中。

多路处理模块(MPM)必须在编译配置时就进行选择,之前笔者在编译 httpd 源码包时用过选项“--with-mpm=”,它的作用就是指定服务器默认支持哪一种多路处理模块(MPM)。在安装完成 httpd 后,还可以通过 httpd -V 命令查看目前 Apache 采用的是哪种MPM。

注意,httpd-2.2 版本默认的 MPM 为 prefork,而 httpd-2.4 版本默认的 MPM 是 event,具体可以通过 httpd -V 命令查看。

常用的 MPM 如下:
在企业中常见的多路处理模块(MPM)有 3 种,分别是 prefork、worker和event。

prefork

下图给出了 prefork 的模型架构,prefork 模型属于多进程模型、两级架构,由主进程生成和管理子进程,每个子进程都有一个独立的线程响应用户请求。


图 1 prefork的模型架构

主进程和子进程的主要作用如下:
prefork 模型采用的是预派生子进程的方式,其工作流程如下:当 httpd 服务启动后,主进程会预先创建多个子进程,每个子进程只有一个线程,当接收到客户端请求时,prefork 会将请求交给子进程处理,而子进程在同一时刻只能处理单个请求,如果当前的用户请求数量超过预先创建的子进程数,则主进程会再创建新的子进程来处理额外的用户请求。

注意,在 prefork 模型中,主进程默认会预先创建 5 个子进程,初始创建的子进程数量可以在配置文件中进行修改。

配置文件中与 prefork 相关的配置如下:
<IfModule mpm_prefork_module>
StartServers 5          #初始创建的子进程数量
MinSpareServers 5        #最少预留多少子进程,备用
MaxSpareServers 10       #最多预留多少子进程,备用
MaxRequestWorkers 250    #最多可以允许多少子进程存在
MaxConnectionsPerChild 0 #限制单个子进程在其生存期内要处理的用户连接数,为“0”时子进程将永远不会结束
</IfModule>
prefork 模型属于最古老的一种模式,也是最稳定的模式,优缺点很明显,内存占用相对较高,但是比较稳定。适用于用户访问量不是很高的场景,不建议在高并发场景中使用这种模式。

worker

下图给出了 worker 的模型架构:

图 2 worker的模型架构

worker 模型属于多进程和多线程混合模型、三级架构,由主进程生成和管理子进程,每个子进程生成多个线程,通过线程来响应用户请求。

主进程、子进程和线程的主要作用如下:
worker 模型采用的是混合多进程的模式,工作流程是:httpd 服务启动后,主进程会预先创建多个子进程,这些子进程会创建固定数量的工作线程和一个监听线程,监听线程负责监听用户请求并在请求到达时将其传递给工作线程进行处理,各个线程之间独立处理请求,如果现有的线程总数不能满足当前的用户请求数量,则控制进程将会派生新的子进程并生成线程,来处理额外的用户请求。

在 worker 模型中,主进程默认会预先创建 3 个子进程,每个子进程默认会创建 25 个工作线程,初始创建的子进程和线程数量可以在配置文件中进行更改。

配置文件中与 worker 相关的配置如下:
<IfModule mpm_worker_module>
    StartServers 3        #初始创建子进程的数量
    MinSpareThreads 75     #最小空闲线程数,若空闲的线程数量小于设定值,Apache 会自动建立线程
    MaxSpareThreads 250    #最大空闲线程数,若空闲的线程数量大于设定值,Apache 会自动杀掉多余的线程
    ThreadsPerChild 25      #每个子进程创建的线程数量
    MaxRequestWorkers 400   #最大工作线程总数
    MaxConnectionsPerChild 0 #每个子线程在生命周期内处理的请求数量,到达这个数量子线程会结束。0 表示不结束
</IfModule>
Apache 服务会维护一个空闲的服务线程池,这样可以保证客户端的请求到达后不需要等待,直接由空闲的线程进行处理和响应。

相比 prefork 模型,worker 模型的优势非常明显,因为线程通常会共享父进程的内存空间,所以内存的占用会相对较少,而且在高并发的场景下,worker 模型的表现比 prefork 模型更优秀。

由于线程通常会共享父进程的内存空间,这也会带来一些缺陷,假如一个线程崩溃,则整个子进程及其内所有线程都会受到牵连,这就导致了 worker 模型的稳定性不如 prefork 模型。

worker 模型还有一个缺陷,在使用 keep-alive 长连接的时候,某些线程会一直被占用,即使中间没有请求,也要等待到超时才会被释放,这一问题在 prefork 模型下也存在,如果在高并发场景下过多的线程被占据,就会导致无工作线程可用,从而导致客户端没办法正常使用。例如,用浏览器打开网站时迟迟打不开可能就是因为服务端没有空余的处理资源给予响应。

event

下图给出了 event 的模型架构:

图 3 event的模型架构

event 模型目前是 Apache 的最新工作模式,它是基于事件驱动的模型、三级架构,由主进程负责启动子进程,每个子进程都会按照配置文件中的设置创建固定数量的工作线程及一个监听线程,该线程会监听连接请求并在请求到达时将其传递给工作线程进行处理。

event 模型采用的是“多进程+多线程+事件驱动”的模式,它的出现解决了 worker 模型在 keep-alive 场景下长期被占用线程的资源浪费问题,event 模型会在每个进程的线程中加入一个监听线程来管理这些 keep-alive 类型线程,当有请求到达时,将请求传递给工作线程,执行完毕后,又允许它释放/回收。这样一个线程就可以处理多个请求,实现了异步非阻塞,从而增强高并发场景下的请求处理能力。

配置文件中与 worker 相关的配置如下:
<IfModule mpm_event_module>
    StartServers 3        #初始创建子进程的数量
    MinSpareThreads 75     #最小空闲线程数,若空闲的线程数量小于设定值,Apache 会自动建立线程
    MaxSpareThreads 250    #最大空闲线程数,若空闲的线程数量大于设定值,Apache 会自动杀掉多余的线程
    ThreadsPerChild 25     #每个子进程创建的线程数量
    MaxRequestWorkers 400  #最大工作线程总数
    MaxConnectionsPerChild 0 #每个子线程在生命周期内处理的请求数量,到达这个值后子线程结束。0 表示不结束
</IfModule>
Apache 常用的这 3 种工作模式各自具备优缺点,每个企业的业务场景不同也就注定了所采用的模式不同,这就要考验 Linux 运维工程师的技术能力了,必须根据对应的环境选择适合的模型。

相关文章