Apache 与 Nginx 实际比较

本文翻译自 DigitalOcean 的《Apache vs Nginx: Practical Considerations》

介绍

Apache 和 Nginx 是世界上最通用的两大开源 Web 服务器。他们正为超过 50% 互联网流量传输提供服务。两种解决方案都能处理各种的工作场景,并与其他软件协同工作,从而提供完善的 Web 技术栈。

尽管 Apache 和 Nginx 有许多共通的特性,并不能将它们视为可以完全相互替代的。它们各有所长。了解使用场景对你重新评估选择你的 web 服务器非常重要,这篇文章将专注于讨论两种服务器如何应对不同场景。

概览

在深入比较 Apache 和 Nginx 之前,让我们快速浏览一下这两个项目各自的背景和特点。

Apache

Apache HTTP 服务器由 Robert McCool 在 1995 年创建,并从 1999 年开始在 Apache 软件基金会的指挥下进行开发。由于这个 HTTP web 服务器是基金会的原始项目,并且到目前为止也是他们最流行的软件之一,于是常常将它简称为“Apache”。

自从 1996 年, Apache web 服务器就已经是互联网上最流行的服务器了。正因为如此普及,Apache 从丰富的文章中受益,并被许多其他软件项目集成支持。

之所以管理员们选择使用 Apache,是因为它的灵活、高效以及广泛的支持。它通过一个动态加载模块系统来实现可扩展,并且能在不连接外部软件的情况下,独自处理大量解释型语言。

Nginx

在 2002 年,Igor Sysoev 开始研发 Nginx 来解决 C10K 问题——在当时需要 web 服务器同时处理一万个连接以应对现代网络的需求——这是一个艰难的挑战。它的首个公开版本发布是在 2004 年,通过异步的事件驱动架构得以实现目标。

得益于它的轻量级资源利用,和它在简单硬件设施上的扩展能力,Nginx 自从发布后日益流行。Nginx 擅长于快速处理静态内容,并且它被设计为可向其他软件传递动态请求,这更符合它的开发目的。

之所以管理员们选择使用 Nginx,是因为它的资源利用度以及负载时的响应能力。Nginx 的拥护者欣赏它专注于 web 服务器的核心以及它的代理特性。

连接处理架构

Apache 和 Nginx 最大的不同在于它们对连接和流量传输处理的方式。下文将论述当它们响应不同的流量状况时,表现出的最关键的不同之处。

Apache

Apache 提供一系列多路处理模块(Apache 称这些为 MPMs),用于指示如何处理客户端请求。大致来说,它允许管理员轻松地更换它的连接处理架构。模块如下:

  • mpm_prefork:这个处理模块产生许多单一线程的子进程,每个线程用来处理请求。每个子进程每次处理一个客户端连接。只要请求数小于进程数,MPM 是非常快的。然而,当请求数大于进程数时,性能会下降得非常快。于是在很多场景下,这不是一个好的选择。每个进程会严重影响到内存的消耗,因此 MPM 不利于有效扩展。如果结合其他不是运行在线程上的组件,这也是不错的。比如,PHP 是非线程安全的,于是多路处理模块推荐唯一安全的方法是使用 mod_php 模块来处理这些文件。

  • mpm_worker:这个模块产生许多可管理多线程的进程。每个线程能处理单个连接。这些线程比进程更高效,这意味着这个 MPM 扩展性要好于 mpm_prefork。因为线程数要多于进程数量,这也就意味着新的连接能立刻得到一个空闲的线程,而不用等待进程空闲。

  • mpm_event:这个模块在大多数场景下跟 mpm_worker 模块很相似,但是它能选择是否处理长连接(keep-alive connection)。当使用 mpm_worker 模块,为了保持连接长时间可用,于是无论请求是否是活跃的,其线程会一直被这个连接占用。这个模块保证模块能脱离长连接请求的束缚,从而更快的执行。在 Apache 2.4 版本中这个功能被标记为稳定的。

正如你所见,Apache 提供一个灵活的架构,能够选择不同的连接和请求处理算法。这些备选项主要是随着服务器演变,随着因互联网规模的改变导致请求并发量的需求增长而产生的功能。

Nginx

Nginx 在 Apache 之后进入大众视野,它集中解决并发问题,专注于网站扩展性。随着知识进步,Nginx 使用了一种异步的,非阻塞的,事件驱动的连接处理算法进行全新设计。

Nginx 产生许多工作进程,每个进程处理上千个连接。工作进程通过实现一种快速遍历机制来持续不断地检查和处理事件,以此来完成工作。把实际工作从连接中分离出来,使得每个工作进程只需考虑它自身与从新事件触发得到的新连接。

每个连接被工作进程处理,然后跟其他连接一样被放置到事件循环中。在这个循环里,事件处理是异步的,工作是以一种非阻塞的方式进行的。当连接关闭,它就被移出了循环。

这种连接处理风格致使 Nginx 可以用有限的资源进行惊人地扩展。因为 Nginx 服务器是单线程的,并且进程不会产生子进程去处理新的连接,所以内存和 CPU 占用会相对稳定,即使是在高负载的情况下。

静态与动态内容

以真实世界的案例来说,最通常比较的是 Apache 和 Nginx 如何处理静态和动态内容的请求。

Apache

Apache 服务器使用传统的基于文件的方法来处理静态内容。这个操作的性能主要依赖上文提到的 MPM 方法的功能。

Apache 也能够处理动态内容,通过在它的每个工作实例嵌入相关语言的处理器。这使它可以在 web 服务器内部执行动态内容,而无需依赖外部组件。这些动态处理器能够通过使用动态载入模块来启动。

Apache 的动态内容处理能力意味着配置动态处理更简单了。通信不需要使用额外软件,而且如果内容需求有变化,模块可以很容易地更换。

Nginx

Nginx 本身没有处理动态内容的能力。为了处理 PHP 以及其他生成动态内容的请求,Nginx 必须传递信息给外部处理器来执行,并且等待渲染内容被传输回来。最终结果将返回给客户端。

对于管理员,这意味着 Nginx 与处理器之间的通信必须配置成 Nginx 所知的协议(如 http,FastCGI,SCGI,uWSGI,memcache)。这会让事情变得略为复杂,特别是当你想要预测最大连接数的时候,因为还存在一个额外的用于调用处理器的连接。

然而,这个方法也有一些优点。因为动态解释器不内嵌在工作进程里,所以它的上游只代表动态内容。静态内容会被直接访问,所以解释器只会在需要的时候才会被调用。Apache 也能够实现这种做法,但是为了做到这样将损失上文提到的那些好处。

分布式与中心化配置

对于管理员,两个软件间最显而易见的不同是内容目录中是否允许目录级别的配置。

Apache

Apache 有一个选项,允许每个目录包含额外的配置。它从中自动检查并解析隐藏文件中的指令。这些文件被叫做 .htaccess

因为这些文件属于每个内容目录,所以当处理一个请求时,Apache 会检查请求文件路径中的每个部分,为了执行 .htaccess 文件里面的指令。这使 web 服务器有效进行去中心化的配置,通常被用来实现修改 URL,访问限制,授权和验证,甚至缓存策略。

然而以上所说的可以全部写在 Apache 的主配置文件中,.htaccess 文件有许多重要的特性。首先,由于它每次根据请求路径来解析文件,所以无需重启服务器就可以立刻生效配置。其次,它允许未授权用户部分控制他们自己的网络内容,而无需给他们操作整个配置文件的权限。

这对于一些网络软件来说是个很轻松的方法。比如内容控制系统(CMS),无需访问中心配置文件就能够调整它们的环境配置。这也能让共享主机提供商保留主配置的控制权的同时,让客户去操作他们自己的独立目录。

Nginx

Nginx 不会解析 .htaccess 文件,也不会提供类似机制去处理脱离主配置文件的各个目录配置。相比 Apache 模型这样少了很多灵活性,但是它有自己的优势。

相对于 .htaccess 系统的目录级别配置,Nginx 最值得注意的改进是性能提升。传统的 Apache 服务器设置允许在于任何目录下设置 .htaccess 文件。于是每次请求时,服务器会在被请求文件的每个父目录下查找这些文件。如果发现了一个或多个 .htaccess 文件,它们一定会被读取并被解析。而 Nginx 并不允许目录覆盖,它通过在唯一的目录下查找匹配,读取文件——假设这个文件是在传统目录架构下的——来更快地响应请求。

另一个优势是相对安全。分布式目录式配置访问的同时也把安全职责分布给了每个独立用户,这些用户也许不能可靠地处理好任务。保证管理员维护整个 web 服务器,能防止一些因为把权限交给别人而引发的安全疏漏。

请记住,如果你担心那些安全隐患,你也可以让 Apache 停止解析 .htaccess

文件与基于 URI 的解析

Web 服务器如何解析请求并将它们映射到系统实际资源,是比较两个服务器的另一个重点。

Apache

Apache 可以将一个请求解析为文件系统中的一个物理资源,或者一个需要更多抽象计算的 URI 地址。通常对于前者,Apache 使用 <Directory> 或者 <Files> 块,其利用 <Location> 块来代表更多抽象资源。

因为 Apache 完全被设计成一种 web 服务器,默认就是把请求解析为文件系统资源。一开始它表示一个文件根节点,然后在其末尾增加请求的主机和端口部分,用来查询一个真实文件。总的来说,文件系统的分层结构在网络上体现为一个可访问的文件树。

当请求没有匹配到文件,Apache 提供一系列可选项。比如,Alias 命令能够用来映射到一个可选的地址。使用 <Location> 块可以作为 URI 的访问方法来代替文件系统。还可以使用正则表达式变体通过文件系统让配置变得更加灵活。

尽管 Apache 有能力去操作文件系统和网络空间,它还是重度依赖于文件系统的操作方法。这在一些设计决策得以体现,包括使用 .htaccess 文件来配置每个目录。Apache 文档里提醒到,当镜像请求底层文件系统时,使用基于 URI 的块来限制访问。

Nginx

Nginx 被设计为 web 服务器和代理服务器。由于这两个架构要求,Nginx 主要使用 URI 工作,在必要时可以将它映射到文件系统上。

这体现在一些 Nginx 配置文件的构造与解析中。Nginx 没有提供可以给特定文件目录指定配置的机制;相对的,它能够解析 URI。

例如,Nginx 的主配置块是 serverlocation 块。当 location 块匹配到 URI 主机和端口后面的部分,server 块就用来解析被请求的主机。在这时,请求被解析为 URI,而不是文件系统的地址。

对于静态文件,所有请求最终会被映射到文件系统上的地址。首先,Nginx 选择一个服务器和地址块来处理请求,然后将文件根节点绑定到这个 URI 上,根据配置文件所指示的修改某些东西。

这也许似曾相识,不过却是主要使用 URI 而不是文件地址来解析请求,这样使得 Nginx 更容易实现 web 服务器、邮件服务器以及代理服务器的角色。通过列出如何响应不同的请求模式,让 Nginx 配置起来很简单。在 Nginx 准备好去处理某个请求前,它不会去检查文件系统,这解释了为什么它不实现 .htaccess 这种形式的文件。

模块

Nginx 和 Apache 都可以通过模块系统进行扩展,但是它们的运作方式是完全不同的。

Apache

Apache 的模块系统允许你动态加载或拆卸模块,以保证在服务器运行时满足你的需求。当模块被开启或关闭时,当附属功能挂载到主服务器或者从主服务器移除时,Apache 内核是始终运行着的。

Apache 使用这个功能实现了大量复杂的任务。因为这个系统已发展成熟,所以有丰富的模块库可供使用。你也可以使用模块来改写一些核心功能,比如 mod_php,这个模块用来给正在运行中的工作进程内嵌 PHP 解释器。

然而模块没有限制处理动态内容。在其他功能中,它们可以被用来重写 URL,验证客户端,加强服务器,日志,缓存,压缩,代理,限流,加密。动态模块能扩展核心功能而无需顾虑太多额外工作。

Nginx

Nginx 也实现了模块系统,但是它与 Apache 系统非常不一样。在 Nginx 中,模块不是动态加载的,因此必须事先选择好并将它们编译到核心软件中。

对于许多用户来说,这降低了 Nginx 的灵活度,尤其是对那些不负责编译软件的用户来说。因为分发的包通常只包含通用的模块,如果你想要使用非标准的模块,就不得不自己编译源码来构建服务器。

尽管如此,Nginx 模块依然非常有用,你可以自己决定在你的服务器之外加载任何想要的功能。有些用户也许更在意安全性,因为组件不应该随意地挂载到服务器上。然而,如果你曾经把服务器放在一个可以随意挂载的机器上,那么它可能已经受到了损害。

Nginx 模块拥有许多同 Apache 模块一样的能力。例如,Nginx 模块支持代理,压缩,限流,日志,重写,地理位置,验证,加密,流式处理,以及邮件功能。

技术支持,兼容性,生态系统,以及文档

重点要考虑的是启动和运行过程中,其他软件的支持程度,以及可以获得多大帮助。

Apache

因为 Apache 流行已久,对于它的支持是相当普遍的。有大量的官方库,有解释服务器内核的第三方文档,还有根据具体业务场景去结合其他软件调用 Apache 的第三方文档。

随着文档的普及,许多工具和 web 项目包含了能将自己部署在 Apache 环境下的启动工具。这些可能就包含在项目里,亦或你的分发打包团队把它维护在工具包里。

因为它的市场分享以及经过长时间的考验,Apache 通常会从第三方项目中得到更多支持。管理员更愿意选择使用 Apache 进行工作,不仅仅是因为它的流行度,还因为 .htaccess 分布式管理的能力,让很多人在一开始就完全依赖 Apache 来实现主机共享的场景。

Nginx

随着更多用户调整配置 Nginx 的性能,它的支持体验也正在提升,但在某些领域它仍有许多事情要做。

在过去,找一个全面的关于 Nginx 的英文文档十分困难。因为大多数早期开发以及文档都是用俄语完成的。随着项目受到更多关注,文档得以填补,现在在 Nginx 官网上和第三方组织上有许多管理资源。

对于第三方应用,技术支持和说明文档正变得更加可用。有一些项目,包维护者开始给出两个选项,用于自动化配置 Apache 或者 Nginx。即使没有支持,只要项目自身说明了要求(许可,头信息等等),就可以直接配置 Nginx 与其他软件一起工作。

同时使用 Apache 和 Nginx

在浏览过 Apache 和 Nginx 的各种优势与限制之后,你也许对使用哪个服务器更符合你的需求有了更好的主意。然而,许多用户发现也许可以权衡两者,让它们共同发挥各自的威力。

传统使用它们的方法,是将 Nginx 作为反向代理放在 Apache 前面。这将使 Nginx 处理所有客户端请求。充分利用 Nginx 快速响应的优势,以及它处理大量并发连接的能力。

对于静态内容,Nginx 擅长快速将文件直接返回给客户端。对于动态内容,比如 PHP 文件,Nginx 将把请求转发给 Apache,Apache 处理好后把渲染好的页面返回。Nginx 再将结果返回给客户端。

这种配置对于许多用户来说运作良好。因为它将 Nginx 作为一个排序机器,处理所有请求,然后将它本身难以处理的请求传递给 Apache。通过减少给 Apache 服务器的请求,我们能够缓解一些 Apache 进程或者线程发生阻塞的情况。

这种配置让你能够通过增加必要的后端服务器来横向扩展。Nginx 能够轻松地配置成传递请求给服务器集群。以此增强配置来抵御故障,提高性能。

结论

如你所见,Apache 和 Nginx 都是很强力、灵活、能干的。决定哪个服务器对你来说最好,主要取决于什么功能可以解决你的特定需求,然后用你的方式去测试它。

这两个项目在原生性能、功能,以及启动和运行每个解决方案所需的必要时间上有非常现实的冲突。然而,这些都是一系列取舍后的结果,不应该不假思索地随意使用。最后,没有通用的适合所有场景的 web 服务器,所以选择最切合你的目标的解决方案吧。