MPM模块详解

-- TOC --

MPM(Multi-Processing Module)就是Apache的并行处理模块,一定要好好配置选择,直接影响网络服务的性能。

Apache2.4默认的MPM配置的是event模式。

MPM模式一共有3种:

  1. 最早的prefork模式;
  2. 后来的worker模式;
  3. 最先进的event模式。

prefork模式是纯进程模式,资源消耗比较大,比较稳定;worker模式是进程和线程混合,提高了资源利用率;event模式是最新的,在apache2.4中,成为了默认模式,这个模式是为了应对访问负载非常重,且需要高并发的服务器而设计的,在源码安装Apache的时候,通过--with-mpm选择模式。

查看已安装的httpd的mpm模式:path/to/apachectl -l

一般情况下,mpm模块都是默认打开的,您可以检查下httpd.conf文件:

Include conf/extra/httpd-mpm.conf

prefork

prefork是apache这个著名的web server最早支持的模式,顾名思义,prefork就是提前fork子进程。

Apache在启动之初,会预先fork一些子进程,然后等待请求进来。之所以这样做,是为了减少频繁创建和销毁进程的系统开销。每个子进程只有一个执行线程,在同一时刻,只能处理一个请求。(提前创建子进程,就像FastCGI)

优点:成熟稳定,兼容所有新老模块,进程间内存空间相互独立,不存在同步开销。

缺点:一个进程相对占用更多的系统资源,会消耗更多的内存。而且,prefork模式不擅长处理高并发请求,在高并发的这种场景下,prefork会将请求放进队列中,一直等到有可用进程,请求才会被处理。

现在基本上已经很少会用到prefork模式了。

worker

prefork模式是预派生进程,系统开销较大,而worker模式可以理解为预派生线程,减少系统开销,更好的支持高并发场景。

worker模式也会预先fork多个子进程(数量比较少),然后每个子进程再创建更多的线程(Thread),同时包括一个监听线程。每个请求过来,会被分配到1个线程来服务。线程比起进程会更轻量,系统开销更少,因为线程能够共享父进程的内存空间,这样内存的占用会减少一些。在高并发的场景下,因为比起prefork有更多的可用线程,表现会更优秀一些。

相当于还是每个进程都在listen(SO_REUSEPORT),Linux负责在进程间负载分担,每个httpd进程负载在自己的线程池里面分配具体任务。

有些人会觉得奇怪,那么这里为什么不完全使用多线程呢,还要引入多进程?因此如果只有一个进程listen,listen的进程会成为瓶颈;如果多个线程listen,在分配具体任务到其它线程的时候,相互之间也会存在更复杂的同步,这也是开销。另外,这也是为了安全,如果有进程异常挂掉,还有别的进程在处理请求。

worker.jpg

优点:线程占据更少的内存,相同内存下可以创建更多线程等待请求,高并发下表现更优秀。

缺点:必须考虑线程同步的问题。

event

event模式是Apache MPM模块中最新的模式,从2.4版本开始,event模式就成了默认模式。

event模式和worker模式比较相似,也是由父进程创建子进程,子进程再创建一堆线程来处理请求。它们最大的区别在于,event试图解决在keep-alive的场景下,线程资源长期被占用的问题。在event模式中,会有一个专门的线程来管理这些keep-alive类型的连接,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,允许它释放。这样增强了高并发场景下的请求处理能力。

event模式在遇到某些不兼容的模块时会失效,并会自动回退到worker模式。官方自带的模块,全部是支持event MPM的。以前很多人说event模式不支持https,现在其实也是支持的。

event.jpg

为什么event模式能够更好地支持高并发的场景?

worker模式下,每个线程都可以接收请求,由于keep-alive机制,单独一个线程对于多个请求只能顺序的处理。而在event模式中,接收请求的线程不做其它事情,只负责将处理的任务分给其它线程,假设在一个HTTP的连接中(其实就是TCP连接),接收线程同时(理论上应该是连续)收到10个请求,接收线程会将这10个请求分给10个不同的线程处理,这样并发性能就会更好。

HTTP/1.1的队头阻塞?

因此,event模式在面对高并发场景时,性能更好。但是,event模式性能的释放,也依赖于底层的硬件支持。在配置一般的服务器上,event模式的性能不一定能释放出来。我见过用ab做的性能测试,在其它条件都一样的情况下,event模式的性能反而略逊于worker模式。

优化配置Apache的MPM模块(Worker模式)

优化配置Apache的MPM模块,只针对Worker模式。下面的英文来自Apache官方文档,中文是翻译和个人理解说明。

A single control process (the parent) is responsible for launching child processes. Each child process creates a fixed number of server threads as specified in the ThreadsPerChild directive, as well as a listener thread which listens for connections and passes them to a server thread for processing when they arrive.

在Worker模式下,Apache启动之后,会先启动一个control process(父进程),再由这个父进程按照配置文件里面的参数启动子进程。

每个子进程创建一组固定数量的服务线程,这个固定的数量由ThreadsPerChild参数(默认25)确定;同时,还会创建一个监听线程(listener thread)。也就是说,每个子进程创建ThreadsPerChild+1个线程。

Apache HTTP Server always tries to maintain a pool of spare or idle server threads, which stand ready to serve incoming requests. In this way, clients do not need to wait for a new threads or processes to be created before their requests can be served. The number of processes that will initially launch is set by the StartServers directive. During operation, the server assesses the total number of idle threads in all processes, and forks or kills processes to keep this number within the boundaries specified by MinSpareThreads and MaxSpareThreads. Since this process is very self-regulating, it is rarely necessary to modify these directives from their default values. The maximum number of clients that may be served simultaneously (i.e., the maximum total number of threads in all processes) is determined by the MaxRequestWorkers directive. The maximum number of active child processes is determined by the MaxRequestWorkers directive divided by the ThreadsPerChild directive.

Apache HTTP Server总是试图保持一定数量的空闲服务线程,这样在有请求到来的时候,可以理解处理,而不是先创建新的子进程和服务线程。

在服务器运行过程中,Apache会自动的创建或删除子进程,将服务器线程的总数量控制在MinSpareThreads和MaxSpareThreads之间。这个过程是自动的,在服务器负载不高的时候进行,而且,几乎没有必要去修改这两项配置的默认值。MinSpareThreads的默认值也是25,这里有个细节,即要理解Apache创建的每个子进程都有固定数量的线程,默认是25个,那么MinSpareThreads和MaxSpareThreads这两个参数的值也应该是ThreadsPerChild的整数倍。一般不要修改ThreadsPerChild的值。

StartServers参数决定了在Apache HTTP Server启动的时候,创建的子进程数。如果StartServers设置为1,启动Apache后,你会看到2个httpd进程,其中一个是控制进程。

MaxRequestWorkers参数用来控制最大可以并发处理的服务线程数,这个值同样也需要是ThreadsPerChild的值的整数倍。因此,Apache能够创建的最大子进程说可以用MaxRequestWorkers/ThreadsPerChild得到。

Two directives set hard limits on the number of active child processes and the number of server threads in a child process, and can only be changed by fully stopping the server and then starting it again. ServerLimit is a hard limit on the number of active child processes, and must be greater than or equal to the MaxRequestWorkers directive divided by the ThreadsPerChild directive. ThreadLimit is a hard limit of the number of server threads, and must be greater than or equal to the ThreadsPerChild directive.

ServerLimit参数,顾名思义,决定了最大子进程的数量,这个值必须大于等于MaxRequestWorkers/ThreadsPerChild。

ThreadLimit参数,顾名思义,服务线程的最大数,这个值必须大于等于ThreadsPerChild。

这两个参数在实际配置中,可能不会出现,仔细想想,这两个参数的确是有些多余的。在改变配置的时候,这些参数可以帮助我们减少配置量,并防止出错。

In addition to the set of active child processes, there may be additional child processes which are terminating, but where at least one server thread is still handling an existing client connection. Up to MaxRequestWorkers terminating processes may be present, though the actual number can be expected to be much smaller. This behavior can be avoided by disabling the termination of individual child processes, which is achieved using the following:

  1. set the value of MaxConnectionsPerChild to zero
  2. set the value of MaxSpareThreads to the same value as MaxRequestWorkers

子进程还有可能被终止,当此子进程中最后一个服务线程工作完成之后。

比如MaxRequestWorkers/ThreadsPerChild=8,表示系统最大可以启动8个子进程,在繁忙过去后,系统要终止一些子进程,让服务线程的数量维持在MinSpareThreads和MaxSpareThreads之间。

可以通过如下配置,使子进程不会被kill:

  1. 将MaxConnectionsPerChild设置为0;
  2. 让MaxSpareThreads=MaxRequestWorkers;

MaxConnectionsPerChild controls how frequently the server recycles processes by killing old ones and launching new ones.

MaxConnectionsPerChild参数表示每个子进程处理多少个请求之后,就会被终止。如果你的服务器有多余1个子进程,建议将MaxConnectionsPerChild设置为一个常数,比如100000,这表示在处理了100000个请求之后,这个子进程会被终止,这样可以有效防止可能的系统内存泄露。如果你的服务器资源只够开1个子进程,就可以考虑按上诉建议配置,子进程永不终止。

下面是官方教材给出的一个典型配置:

ServerLimit         16
StartServers         2
MaxRequestWorkers  150
MinSpareThreads     25
MaxSpareThreads     75
ThreadsPerChild     25

以上配置MaxRequestWorkers/ThreadsPerChild=6,而ServerLimit=16,16个子进程是达不到的;但是如果将MaxRequestWorkers配置成1000,这时MaxRequestWorkers/ThreadsPerChild=40,而ServerLimit=16,这时最大也就16个子进程,MaxRequestWorkers这个配置上限就达不到。

While the parent process is usually started as root under Unix in order to bind to port 80, the child processes and threads are launched by the server as a less-privileged user. The User and Group directives are used to set the privileges of the Apache HTTP Server child processes. The child processes must be able to read all the content that will be served, but should have as few privileges beyond that as possible.

父进程通常是root启动的,这样权限才足够去绑定80端口。而子进程是父进程启动的,权限较低。httpd.conf配置文件中,通过User和Group指令来制定子进程的用户和组信息。这就是为什么网站资源文件都要给这个User和Group授权。

MaxRequestWorkers(原MaxClients)

Apache的MPM模块的配置,对服务器的稳定运行至关重要,MaxRequestWorkers就是一条重要的配置,在2.3.13之前,它的名字是MaxClients。现在的Apache版本,这两个名字的配置向都支持,是一样的含义。

Description: Maximum number of connections that will be processed simultaneously

MaxRequestWorkers,就是能够同时处理的最大连接数,或者我们说成最大并发数。

The MaxRequestWorkers directive sets the limit on the number of simultaneous requests that will be served. Any connection attempts over the MaxRequestWorkers limit will normally be queued, up to a number based on the ListenBacklog directive. Once a child process is freed at the end of a different request, the connection will then be serviced.

任何高于这个数量的连接,会放入队列。

For non-threaded servers (i.e., prefork), MaxRequestWorkers translates into the maximum number of child processes that will be launched to serve requests. The default value is 256; to increase it, you must also raise ServerLimit.

对于prefork模式,MaxRequestWorkers表示的是最大子进程数。

For threaded and hybrid servers (e.g. event or worker) MaxRequestWorkers restricts the total number of threads that will be available to serve clients. For hybrid MPMs the default value is 16 (ServerLimit) multiplied by the value of 25 (ThreadsPerChild). Therefore, to increase MaxRequestWorkers to a value that requires more than 16 processes, you must also raise ServerLimit.

对于worker和event模式,MaxRequestWorkers会限制总的服务线程数。

本文链接:https://cs.pynote.net/net/httpd/202204051/

-- EOF --

-- MORE --