Apache 2.0:全新改进的内部结构

作者:Ibrahim F. Haddad

Apache 项目是一项协作软件开发工作,旨在创建一个健壮、商业级且免费提供的 HTTP Web 服务器源代码实现。该项目由一群位于世界各地的志愿者共同管理,他们使用互联网和 Web 进行沟通、计划和开发服务器及其相关文档。这些志愿者被称为 Apache 组织。此外,数百名用户为该项目贡献了想法、代码和文档。

根据 Netcraft Web 服务器调查,自 1996 年 4 月以来,Apache 一直是互联网上最受欢迎的 Web 服务器。这并不令人意外,因为它具有许多特点,例如能够在各种平台上运行、可靠性、健壮性、可配置性以及它是免费的且文档完善。Apache 比其他 Web 服务器有许多优势,例如提供完整的源代码和非限制性许可证。它也功能齐全。例如,它符合 HTTP/1.1 标准,并且可以通过第三方模块进行扩展,它还提供自己的 API 以允许模块编写。使其成为受欢迎的 Web 服务器的其他有趣功能包括能够为不同的错误定制特定的响应、支持虚拟主机、URL 重写和别名、内容协商以及支持可配置的、可靠的管道日志,这允许用户以他们想要的格式生成日志。

从 1.3 到 2.0 的飞跃

Apache 1.3 一直是一个性能良好的 Web 服务器,但它也存在一些缺点,例如在某些平台上的可扩展性。例如,根据 Martin Pool 的说法,AIX 进程是重量级的,一台小型 AIX 机器服务几百个并发连接就可能变得负载过重。在这种情况下,使用进程不是最有效的解决方案,需要一个线程 Web 服务器。

此外,随着对 Web 服务器的要求不断发展,需要更高的可靠性、更高的安全性和更高的性能等新功能。作为回应,Web 服务器必须不断发展以满足这些需求。Apache 也不例外,它继续努力使其新的 2.0 版本(参见侧边栏)成为一个更健壮、更快速的 Web 服务器。

侧边栏

可移植性

Apache 以其可移植性而闻名,因为它可以在多个平台上工作。然而,让 Apache 的相同基础代码在如此多的平台上可移植带来了很高的代价,即维护的难度。Apache 服务器已经达到了一个程度,即将其移植到其他平台变得越来越复杂。因此,为了赋予 Apache 在未来更多平台上生存所需的灵活性,这个问题必须得到解决。因此,Apache 将能够使用专门的 API(在可用的情况下)来提供改进的性能,从而使其易于移植到新平台。

Apache 可移植运行时

Apache 最初旨在在标准 UNIX 系统上工作。然而,它对其他平台的支持不断增长,支持的平台数量影响了源代码的简洁性。一个影响是代码广泛使用条件编译来应对平台特性。在某些提供不合格实现或更快路径的平台上,编写标准 POSIX API 也是不可取的。

为了解决这些问题,Ryan Bloom 正在领导开发一个解决方案,一个名为 Apache 可移植运行时 (APR) 的层。APR 为服务器应用程序提供了一个标准的编程接口,涵盖了文件 I/O、日志记录、互斥、共享内存以及管理子进程和异步 I/O 等任务。APR 将应用程序与标准实现中的不兼容性隔离开来,因此它将使用最有效的方式在每个受支持的特定平台上实现每个功能。

另一个有助于解决可移植性问题的组件是 Ralph Engelschall 的 MM 库,它隐藏了在进程之间设置共享内存区域的细节,并提供了一个类似于 malloc 的接口来操作它们。

MM 库是一个双层抽象库,它简化了 UNIX 平台下派生进程之间共享内存的使用。在第一层(下层),它隐藏了所有平台相关的实现细节(分配和锁定)。在处理共享内存段时,在第二层(上层),它为在这些共享内存段内处理数据结构提供了一个高级的 malloc(3) 风格的 API,这是一种方便且众所周知的方式。

传统的 Apache 结构基于一个父进程和一组可重用的子进程(参见图 1)。父进程读取配置并管理子进程池。每个子进程在任何时候要么正在处理单个请求,要么处于休眠状态。Apache 1.x 自动调节子进程池的大小,以便有足够的子进程来应对负载高峰,而不会使用过多的资源来维护空闲进程。繁忙的子进程在单个套接字上一次处理一个请求。

Apache 2.0: The Internals of the New, Improved

图 1. 传统的 Apache 结构

一些网站负载很重,每分钟甚至每秒接收数千个请求。传统的 TCP/IP 服务器会派生一个新的子进程来处理来自客户端的传入请求。然而,在繁忙网站的情况下,派生大量子进程的开销会使服务器窒息。因此,Apache 使用了一种不同的技术。它从一开始就派生固定数量的子进程。子进程独立地服务传入请求,使用不同的地址空间。Apache 可以根据当前负载动态控制它派生的子进程数量。

这种设计运行良好,并被证明既可靠又高效;其最好的特性之一是服务器可以在子进程死亡后幸存下来,并且也很可靠。它也比为每个请求派生一个新子进程的规范 UNIX 模型更有效。

这种传统的 Apache 设计在现代 UNIX 系统上可以很好地处理相当高的负载。特别是在 Linux 中,上下文切换和派生新进程的成本很低,因此这种简单的设计几乎是最优的。然而,进程之间隔离的一个缺点是它们不容易共享数据,因此跨服务器共享会话数据需要做一些工作。

另一种方法是在单独的线程中处理每个请求:这是大多数基于 NT 的 Web 服务器使用的模型。虽然这种方法消除了任务之间的大部分保护,但它允许模块程序员更大的灵活性,并且在线程比进程成本更低的系统(例如 Windows NT 和 AIX)上,它可以更快。

Apache 2.0 引入了 MPM(多进程处理模块),它从大多数代码中隐藏了进程模型。在运行时,Apache 可以配置为使用线程、进程、两者的混合或其他模型。模块可以注册新的进程模型以适应其操作系统或应用程序。一个提议的例子是派生以不同用户身份运行的进程,以便在为多个客户提供虚拟主机的机器上提高安全性。

性能

Apache 的开发者一直强调服务器的安全性、正确性和灵活性。然而,截至 Apache 1.3,在将性能提升到与其他高端 Web 服务器相当水平方面所做的努力很少。随着 Web 流量的持续爆炸式增长,Apache 2.0 试图在不损害其其他特性的情况下提高其吞吐量。

Web 服务器有几个关键的性能决定因素。其中一些因素包括可用于保存文档树的内存量、磁盘带宽、网络带宽和 CPU 周期。在大多数情况下,人们会添加或升级硬件以提高其 Web 服务器的性能。然而,随着互联网的爆炸式增长及其在我们生活中日益重要的作用,互联网上的流量每六个月增长超过 100%。因此,服务器上的工作负载正在迅速增加,这些服务器很容易过载。除了硬件升级或添加之外,还存在几种选项可以克服这个问题。

对于非常繁忙的 Web 服务器,切换任务和执行 I/O 的内核开销成为一个问题。Apache 通过 mod_mmap_static 模块为高流量问题提供了一个解决方案。该模块将文件绑定到虚拟内存空间,并避免了“open”和“read”系统调用从文件系统中拉取它们的开销。当服务器有足够的内存来缓存整个文档树时,此过程可以显着提高速度。

此外,为了提高性能并每秒处理更多请求,管理员可以运行一个专门的 Web 服务器来处理简单的请求,并将所有其他请求传递给 Apache。另一种减少操作系统开销的方法是在内核本身中内置一个小型 HTTP 服务器。这两种方法将在稍后讨论(参见 HTTPD 加速器)。

I/O 分层

版本 1.3 及之前的 Apache 模块直接写入回客户端的 TCP 连接。这种安排简单而有效,但缺乏灵活性。

这种不灵活性的一个例子是通过 SSL 的安全事务。为了执行加密通信,SSL 模块必须拦截客户端和处理程序模块之间的所有流量。由于没有抽象层,这项任务很困难,而 1990 年代的密码学法律禁止添加方便的钩子,这使得任务更加困难。想要运行安全站点的管理员可以选择将不优雅的补丁集应用于 Apache 源代码,或者使用专有的且可能不兼容的二进制发行版。

在 Apache 2.0(使用 APR)中,所有 I/O 都通过抽象 I/O 层对象完成。这种安排允许模块相互挂钩到彼此的流中。然后,SSL 可以通过正常的模块接口实现,而无需特殊的钩子。I/O 层还可以通过提供一个标准的位置来进行字符集转换来帮助国际化站点。

此外,在以后的 Apache 版本中,I/O 层可能会支持“最常请求的模块”功能,该功能将使一个模块过滤另一个模块的输出。但是,这可能不会在 Apache 2.1 中发生。

多进程处理模块 (MPM)

创建 Apache 2.0 的最初原因是解决可扩展性问题。第一个提出的解决方案是拥有一个混合 Web 服务器,一个同时具有进程和线程的服务器。此解决方案提供了并非所有内容都在一个进程中的可靠性,以及线程提供的可扩展性。然而,这种方法没有完美的方法将请求映射到线程或进程。

例如,在 Linux 上,最好有多个进程,每个进程都有多个线程来处理请求。如果单个线程死亡,服务器的其余部分将继续服务更多请求,并且服务器不会受到影响。在不能很好地处理多个进程的平台(例如 Windows)上,需要一个具有多个线程的进程。另一方面,必须考虑没有线程支持的平台,因此有必要继续使用 Apache 1.3 的预派生进程来处理请求的方法。

映射问题可以通过多种方式处理,但最理想的方式是增强 Apache 的模块功能。这就是引入多进程处理模块 (MPM) 背后的原因。MPM 决定了如何将请求映射到线程或进程。大多数用户永远不会编写 MPM,甚至不知道它们的存在。每个服务器使用一个 MPM,给定平台的正确 MPM 在编译时确定。

目前,有五个 MPM 可用选项。所有这些选项,除了可能的 OS/2 MPM 之外,都保留了 Apache 1.3 中的父/子关系,这意味着父进程将监视子进程并确保有足够数量的子进程正在运行。

MPM 提供了两个重要的好处

1. Apache 可以更干净、更高效地支持各种操作系统。特别是,Windows 版本的 Apache 现在效率更高,因为 mpm_winnt 可以使用本机网络功能来代替 Apache 1.3 中使用的 POSIX 层。此优势也扩展到其他实现专用 MPM 的操作系统。

2. 服务器可以更好地根据特定站点的需求进行定制。例如,需要大量可扩展性的站点可以选择使用线程 MPM,而需要稳定性或与旧软件兼容性的站点可以使用“预派生”MPM。此外,还可以提供在不同用户 ID(perchild)下服务不同主机的特殊功能。

prefork MPM 实现了一个非线程的、预派生的 Web 服务器,它以类似于 Apache 1.3 在 UNIX 上的默认行为的方式处理请求。单个控制进程负责启动子进程,这些子进程侦听连接并在连接到达时处理它们。

Apache 始终尝试维护几个备用或空闲服务器进程,这些进程已准备好处理传入请求。这样,客户端无需等待派生新的子进程即可处理其请求。

StartServers、MinSpareServers、MaxSpareServers 和 MaxServers(在 /etc/httpd.conf 中设置)控制父进程如何创建子进程来处理请求。通常,Apache 是自我调节的,因此大多数站点不需要从其默认值调整这些指令。需要处理超过 256 个并发请求的站点可能需要增加 MaxClients,而内存有限的站点可能需要减少 MaxClients 以防止服务器抖动。

虽然父进程通常以 root 身份在 UNIX 下启动,以便绑定到端口 80,但子进程由 Apache 以权限较低的用户身份启动。User 和 Group 指令用于设置 Apache 子进程的权限。子进程必须能够读取将要服务的所有内容,但除此之外应尽可能少地拥有权限。

MaxRequestsPerChild 控制服务器回收进程的频率,方法是杀死旧进程并启动新进程。

PTHREAD MPM 是大多数类 UNIX 操作系统的默认设置。它实现了一个混合多进程多线程服务器。每个进程都有固定数量的线程。服务器通过增加或减少进程数量来调整以处理负载。

单个控制进程负责启动子进程。每个子进程创建固定数量的线程,如 ThreadsPerChild 指令中所指定的那样。然后,各个线程侦听连接并在连接到达时处理它们。PTHREAD MPM 应该在支持线程且可能在其实现中存在内存泄漏的平台上使用。这也可能是具有用户级线程的平台的适当 MPM,尽管此时的测试不足以证明此假设。

当使用 DEXTER MPM 编译时,服务器启动时会派生静态数量的进程,这些进程在服务器的生命周期内不会更改。每个进程将创建特定数量的线程。当请求进入时,一个线程将接受并响应它。当子进程看到其太多线程正在处理请求时,它将创建更多线程并使其可用于处理更多请求(参见图 2)。

Apache 2.0: The Internals of the New, Improved

图 2. Dexter MPM 模型

DEXTER MPM 应该在大多数能够支持线程的现代平台上使用。它将在 CPU 上创建轻负载,同时处理尽可能多的请求。

WINNT MPM 是 Windows NT 操作系统的默认设置。它使用单个控制进程,该进程启动单个子进程,而子进程又创建线程来处理请求。

PERHILD MPM 实现了一个混合多进程、多线程 Web 服务器。固定数量的进程创建线程来处理请求。负载的波动通过增加或减少每个进程中的线程数来处理。

单个控制进程在服务器启动时启动 NumServers 指令指示的子进程数量。每个子进程创建 StartThreads 指令中指定的线程。然后,各个线程侦听连接并在连接到达时处理它们。

MPM 必须在配置阶段选择并编译到服务器中。如果使用线程,编译器能够优化许多功能,但前提是它们知道正在使用线程。由于某些 MPM 在 UNIX 上使用线程,而其他 MPM 不使用线程,因此如果在配置时选择 MPM 并将其构建到 Apache 中,Apache 的性能将始终更好。

要选择所需的 MPM,您需要将参数 --with-mpm= NAME 与 ./configure 脚本一起使用,其中 NAME 是所需 MPM 的名称(dexter、mpmt_beos、mpmt_pthread、prefork、pmt_os2、perchild)。

一旦服务器被编译,就可以通过使用 % httpd -l 来确定选择了哪个 MPM。此命令将列出编译到服务器中的每个模块,包括 MPM。

以下列表标识了每个平台的默认 MPM

<il>BeOS:mpmt_beos<il>OS/2:spmt_os2<il>UNIX:threaded<il>Windows:winnt

HTTPD 加速器

如前所述,Web 服务器性能可以通过多种方式提高。除了升级运行服务器的硬件外,还可以部署 HTTPD 加速器。用户可以运行一个专门的 Web 服务器来处理简单的静态请求,并将所有其他请求传递给 Apache(或任何其他 Web 服务器),或者在内核本身中内置一个小型 HTTP 服务器。phhttpd (pointy-headed httpd) 和 khttpd (kernel HTTP dæmon) 是这两种方法的概念验证。

phhttpd 从单个进程处理所有请求,并使用“sendfile”系统调用将大部分工作放回内核,此外还解释 HTTP 协议。phhttpd 不能单独运行,因为它需要一个知道如何与 phhttpd 通信的后备完整服务器,例如 Apache。两个服务器在运行时建立通信线路。phhttpd 侦听所有传入连接,如果由于任何原因无法解析请求,它会将其线路上的连接交给 Apache 进行处理。phhttpd 保留一个不经常更改的内容的积极缓存。它使用此内容来减少每个请求必须完成的处理量。它还具有非阻塞事件模型,允许单个线程处理多个连接。线程数可以缩放以匹配托管机器的大小。

为了减少操作系统开销,可以将一个小型 HTTP 服务器放置在内核本身中,以响应对静态文件的请求。它作为模块从 Linux 内核内部运行,仅处理静态网页,并将所有对非静态信息的请求传递给常规用户空间 Web 服务器,例如 Apache。静态网页服务起来并不复杂,但它们很重要,因为几乎所有图像都是静态的,并且很大一部分 HTML 页面也是静态的。常规 Web 服务器对于静态页面几乎没有附加值;它只是一个“将文件复制到网络”的操作,而 Linux 内核非常擅长这一点。

加拿大爱立信研究中心与 Apache 服务器

ARIES(互联网电子服务器高级研究)是一个于 2000 年 1 月在加拿大爱立信研究中心开放架构研究实验室启动的项目。它旨在寻找和原型化必要的技术,以证明集群互联网服务器的可行性,该服务器使用 Linux 和开源软件作为基础技术来展示电信级特性。这些特性包括保证持续可用性、保证响应时间、高可扩展性和高性能。

构建高性能和可扩展的系统需要一个可以跟上每秒数百个请求的 Web 服务器。Apache Web 服务器是我们测试过的三个 Web 服务器之一。我们的测试系统是一个电信级 16 处理器集群,目标是运营商级服务器应用。它在 4 月份的《Linux Journal》中进行了专题报道,我们在其中讨论了我们使用 Linux 虚拟服务器的实验。

在即将发表的文章中,我们将更详细地讨论 Apache 的性能,并评估其他几个 Web 服务器(例如 Jigsaw 和 Tomcat)的性能,以了解它们彼此之间的比较情况。但是,为了让您了解 Apache 2.0 的性能,我们在一个配备 512 MB RAM 的 PIII 服务器节点上安装了 Apache 2.08,并开始使用 WebBench(一个 Web 服务器的基准测试工具)向其生成 HTTP 请求(参见图 3)。

图 3. Apache 2.08 性能

Apache 2.08 能够平均每秒响应 817 个请求,最多可服务 104 个并发客户端。一个重要的因素是,此服务器节点是无盘的,并且 Apache 正在从 NFS 分区(这带来了一些限制)提供文档。在其他测试中,我们注意到当 Apache 服务位于服务器磁盘本身的文档时,性能要好得多(每秒更多请求)。

根据我们的测试,我们认为 Apache 比其他 Web 服务器更快、更稳定、功能更齐全。我们期待测试和试验 2.0 发布版本,该版本承诺提供干净的代码、结构良好的 I/O 分层和增强的可扩展性。

结论

Apache 项目将继续成为一个开源项目,与 HTTP 协议和 Web 发展的总体进步保持同步。该项目背后的人员对修复和改进的建议持开放态度,并且他们会响应大容量提供商以及偶尔用户的需求。Apache 最初的成功部分归功于开发人员和用户进行的众多测试。Apache 组织在发布服务器的任何新版本之前都保持严格的标准,并且当报告错误时,开发人员会尽快发布补丁和新版本。如果您是当前正在使用 Apache 1.3.x 的 IT 经理或系统管理员,我强烈建议在发布版本可用后升级到 Apache 2.0。

致谢

感谢 Martin Pool 允许我们重用他在 AUUG2k 冬季会议上所做的演示材料。

Ibrahim F. Haddad 在加拿大爱立信研究中心工作,研究实时全 IP 网络中的运营商级服务器节点。

资源

Apache 软件基金会:www.apache.org

Apache Week:www.apacheweek.com

IPv6 信息页:www.ipv6.org

内核 HTTP 守护程序:www.fenrus.demon.nl

MM 共享内存库:www.engelschall.com/sw/mm

Netcraft:www.netcraft.com

phhttpd:www.zabbo.net//phhttpd

Ryan Bloom 的 Apache 可移植运行时索引:www.ntrnet.net/~rbb/aprpres

WebBench 基准测试工具:www.zdnet.com

WebDAV:www.webdav.org

加载 Disqus 评论