<-
Apache > HTTP 服务器 > 文档 > 版本 2.4 > 模块

Apache MPM 事件

可用语言:  en  |  fr 

描述worker MPM 的变体,旨在仅为具有活动处理的连接使用线程
状态MPM
模块标识符mpm_event_module
源文件event.c

摘要

event 多处理模块 (MPM) 旨在通过将一些处理工作传递给监听器线程来允许同时服务更多请求,从而释放工作线程来服务新请求。

要使用 event MPM,请在构建 httpd 时将 --with-mpm=event 添加到 configure 脚本的参数中。

Support Apache!

主题

指令

错误修复清单

另请参阅

top

与工作线程 MPM 的关系

event 基于 worker MPM,它实现了混合多进程多线程服务器。单个控制进程(父进程)负责启动子进程。每个子进程根据 ThreadsPerChild 指令中指定的数量创建固定数量的服务器线程,以及一个监听器线程,该线程监听连接并在连接到达时将它们传递给工作线程进行处理。

运行时配置指令与 worker 提供的指令相同,唯一的区别是添加了 AsyncRequestWorkerFactor

top

工作原理

此 MPM 试图解决 HTTP 中的“保持活动问题”。在客户端完成第一个请求后,它可以保持连接打开,使用相同的套接字发送进一步的请求,从而节省创建 TCP 连接的巨大开销。但是,Apache HTTP 服务器传统上会让整个子进程/线程等待来自客户端的数据,这会带来自身的缺点。为了解决这个问题,此 MPM 为每个进程使用一个专用的监听器线程来处理监听套接字、所有处于保持活动状态的套接字、处理程序和协议过滤器已完成其工作的套接字以及唯一剩下的工作是将数据发送给客户端的套接字。

这种利用非阻塞套接字和 APR 公开的现代内核功能(如 Linux 的 epoll)的新架构不再需要配置 mpm-accept Mutex 来避免“惊群”问题。

单个进程/线程块可以处理的连接总数由 AsyncRequestWorkerFactor 指令控制。

异步连接

异步连接需要使用以前的 MPM 固定专用的工作线程,但事件 MPM 不需要。 mod_status 的状态页面在异步连接部分显示了新的列

写入
在将响应发送给客户端时,可能会发生 TCP 写缓冲区已满,因为连接太慢。通常在这种情况下,对套接字的 write() 将返回 EWOULDBLOCKEAGAIN,以便在空闲一段时间后再次变为可写。持有套接字的工作线程可能能够将等待任务卸载到监听器线程,监听器线程反过来将在为套接字引发事件(例如,“套接字现在可写”)后将其重新分配给第一个可用的空闲工作线程。有关更多信息,请查看限制部分。
保持活动
保持活动处理是工作线程 MPM 的最基本改进。一旦工作线程完成将响应刷新到客户端,它就可以将套接字处理卸载到监听器线程,监听器线程反过来将等待来自操作系统的任何事件,例如“套接字可读”。如果客户端收到任何新请求,则监听器将将其转发给第一个可用的工作线程。相反,如果发生 KeepAliveTimeout,则监听器将关闭套接字。这样,工作线程就不负责空闲套接字,并且可以重复使用它们来服务其他请求。
关闭
有时 MPM 需要执行延迟关闭,即在它仍在向 httpd 传输数据时向客户端发送早期错误。发送响应然后立即关闭连接不是正确的方法,因为客户端(仍在尝试发送请求的其余部分)将收到连接重置,并且无法读取 httpd 的响应。延迟关闭是有限制的,但可能需要相当长的时间,因此它被卸载到工作线程(包括关闭挂钩和实际套接字关闭)。从 2.4.28 开始,当连接最终超时时也是如此(监听器线程从不处理连接,除了等待和调度它们的事件)。

这些改进对 HTTP/HTTPS 连接都有效。

优雅进程终止和记分板使用

此 mpm 过去在可扩展性方面存在一些瓶颈,导致以下错误:“记分板已满,未达到 MaxRequestWorkers”。 MaxRequestWorkers 限制了在任何给定时间将要服务的并发请求数,以及允许的进程数 (MaxRequestWorkers / ThreadsPerChild);同时,记分板是所有正在运行的进程及其工作线程状态的表示。如果记分板已满(因此所有线程的状态都不是空闲),但已服务的活动请求数未达到 MaxRequestWorkers,则意味着其中一些线程正在阻止可以服务但已排队的新请求(直到 ListenBacklog 强制执行的限制)。大多数情况下,线程卡在 Graceful 状态,即它们正在等待完成与 TCP 连接的工作以安全终止并释放记分板插槽(例如,处理长时间运行的请求、慢速客户端或启用了保持活动的连接)。两种情况非常常见

从 2.4.24 开始,mpm-event 更加智能,并且能够以更好的方式处理优雅终止。一些改进包括

最后一点中描述的行为可以通过 mod_status 在连接摘要表中通过两列完全观察到:“插槽”和“停止”。前者表示 PID,后者表示进程是否正在停止;额外的状态“是(旧代)”表示进程在优雅重启后仍在运行。

限制

改进的连接处理可能不适用于已声明与事件不兼容的某些连接过滤器。在这些情况下,此 MPM 将回退到 worker MPM 的行为,并为每个连接保留一个工作线程。与服务器一起提供的模块都与事件 MPM 兼容。

当前,对于涉及需要读取和/或修改整个响应主体的输出过滤器的请求,存在类似的限制。如果在过滤器处理数据时客户端连接阻塞,并且过滤器产生的数据量太大而无法在内存中缓冲,则用于请求的线程在 httpd 等待将挂起的数据发送到客户端时不会被释放。
为了说明这一点,我们可以考虑以下两种情况:提供静态资产(如 CSS 文件)与提供从 FCGI/CGI 或代理服务器检索的内容。前者是可预测的,即事件 MPM 可以完全了解内容的结束,并且可以使用事件:提供响应内容的工作线程可以刷新前几个字节,直到返回 EWOULDBLOCKEAGAIN,将剩余部分委托给监听器。反过来,监听器等待套接字上的事件,并将刷新剩余内容的工作委托给第一个空闲工作线程。而在后一种情况(FCGI/CGI/代理内容)中,MPM 无法预测响应的结束,工作线程必须完成其工作才能将控制权返回给监听器。唯一的替代方法是在内存中缓冲响应,但这对于服务器的稳定性和内存占用来说不是最安全的选项。

背景资料

事件模型的实现得益于支持的操作系统中引入了新的 API。

在这些新的 API 可用之前,必须使用传统的 selectpoll API。如果用于处理大量连接或连接集变化率很高,这些 API 会变慢。新的 API 允许监控更多连接,并且在监控连接集频繁变化时性能更好。因此,这些 API 使得编写事件 MPM 成为可能,该 MPM 在典型的 HTTP 模式(大量空闲连接)下扩展性更好。

MPM 假设底层的 apr_pollset 实现是线程安全的。这使 MPM 能够避免过度的高级锁定,或者不必唤醒监听器线程以向其发送保持活动套接字。目前,这仅与 KQueue 和 EPoll 兼容。

top

要求

此 MPM 依赖于 APR 的原子比较和交换操作进行线程同步。如果您正在为 x86 目标编译,并且不需要支持 386,或者您正在为 SPARC 编译,并且不需要在 pre-UltraSPARC 芯片上运行,请将 --enable-nonportable-atomics=yes 添加到 configure 脚本的参数中。这将导致 APR 使用旧 CPU 中不可用的高效操作码来实现原子操作。

此 MPM 在缺乏良好线程的旧平台上性能不佳,但对 EPoll 或 KQueue 的要求使这一点变得无关紧要。

top

AsyncRequestWorkerFactor 指令

描述限制每个进程的并发连接数
语法AsyncRequestWorkerFactor factor
默认值2
上下文服务器配置
状态MPM
模块事件
兼容性在 2.3.13 及更高版本中可用

事件 MPM 以异步方式处理一些连接,其中请求工作线程仅在需要时分配很短的时间,而其他连接则保留一个请求工作线程。这会导致所有工作线程都被占用,并且没有工作线程可用于处理已建立的异步连接上的新工作。

为了缓解这个问题,事件 MPM 做了两件事

此指令可用于微调每个进程的连接限制。一个 **进程** 仅在当前连接数(不包括处于“关闭”状态的连接)低于以下值时才会接受新连接

ThreadsPerChild + (AsyncRequestWorkerFactor * 空闲工作线程数)

可以根据空闲工作线程的平均值计算所有进程的并发连接数的最大估计值

(ThreadsPerChild + (AsyncRequestWorkerFactor * 空闲工作线程数)) * ServerLimit

示例

ThreadsPerChild = 10
ServerLimit = 4
AsyncRequestWorkerFactor = 2
MaxRequestWorkers = 40

idle_workers = 4 (average for all the processes to keep it simple)

max_connections = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit
                = (10 + (2 * 4)) * 4 = 72

当所有工作线程都处于空闲状态时,可以使用更简单的方法计算并发连接数的绝对最大值

(AsyncRequestWorkerFactor + 1) * MaxRequestWorkers

示例

ThreadsPerChild = 10
ServerLimit = 4
MaxRequestWorkers = 40
AsyncRequestWorkerFactor = 2

如果所有进程的所有线程都处于空闲状态,则

idle_workers = 10

我们可以通过两种方法计算并发连接数的绝对最大值

max_connections = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit
                = (10 + (2 * 10)) * 4 = 120

max_connections = (AsyncRequestWorkerFactor + 1) * MaxRequestWorkers
                = (2 + 1) * 40 = 120

调整 AsyncRequestWorkerFactor 需要了解每个特定用例中 httpd 处理的流量,因此更改默认值需要进行广泛的测试,并从 mod_status 收集数据。

MaxRequestWorkers 在 2.3.13 版本之前称为 MaxClients。上述值表明旧名称并未准确描述其在事件 MPM 中的含义。

AsyncRequestWorkerFactor 可以接受非整数参数,例如“1.5”。

可用语言:  en  |  fr 

top

评论

注意
这不是问答部分。此处放置的评论应指向有关改进文档或服务器的建议,如果这些建议已被实施或被认为无效/与主题无关,则可能会被我们的版主删除。有关如何管理 Apache HTTP Server 的问题应发送到我们的 IRC 频道 #httpd(在 Libera.chat 上)或发送到我们的 邮件列表