从运行原理及使用场景看 Apache 和 Nginx

用正确的工具,做正确的事情。

本文只作为了解 Apache 和 Nginx 知识的一个梳理,想详细了解的请阅读文末参考链接中的博文。

Web 服务器

Web 服务器也称为 WWW(WORLD WIDE WEB) 服务器,主要功能是提供网上信息浏览服务。

  • 应用层使用 HTTP 协议。
  • HTML 文档格式。
  • 浏览器统一资源定位器 (URL)。

Web 服务器常常以 B/S(Browser/Server)方式提供服务。浏览器和服务器的交互方式如下:

                 GET /index.php HTTP/1.1

 +---------------+                   +----------------+
 |               +------------------->                |
 |   Browser     |                   |   Server       |
 |               <-------------------+                |
 +---------------+                   +----------------+

                   HTTP/1.1 200 OK
  1. 浏览器向服务器发出 HTTP 请求 (Request)。
  2. 服务器收到浏览器的请求数据,经过分析处理,向浏览器输出响应数据(Response)。
  3. 浏览器收到服务器的响应数据,经过分析处理,将最终结果显示在浏览器中。

Apache 和 Nginx 都属于 Web 服务器,两者都实现了 HTTP 1.1 协议。

Apache

概述

Apache HTTP Server 是 Apache 软件基金会的一个开放源代码的网页服务器,可以在大多数计算机操作系统中运行,由于其跨平台和安全性。被广泛使用,是最流行的 Web 服务器端软件之一。它快速、可靠并且可通过简单的 API 扩充,将 Perl/Python 等解释器编译到服务器中。 – 维基百科

Apache 组件

Apache 是基于模块化设计的,它的核心代码并不多,大多数的功能都被分散到各个模块中,各个模块在系统启动的时候按需载入。

         +----------+
      +- | Module   | -----------------+
      |  +----------+                  |
      |                          +------------+
+-----------+   Apache HTTPD     | php module |
| Module    |                    +------------+
+-----------+              +----------+|
      +----------+-------- |  MPM     |+
                 |         +----+---+-+
               +-v-----------+  |   |
               |    ARP      <--+   |
               +------+------+      |
                      |             |
      +---------------v-------------v--+
      |      Operating  System         |
      +--------------------------------+

MPM(Multi -Processing Modules,多重处理模块)是 Apache 的核心组件之一,Apache 通过 MPM 来使用操作系统的资源,对进程和线程池进行管理。Apache 为了能够获得最好的运行性能,针对不同的平台 (Unix/Linux、Window) 做了优化,为不同的平台提供了不同的 MPM,用户可以根据实际情况进行选择,其中最常使用的 MPM 有 prefork 和 worker 两种。至于您的服务器正以哪种方式运行,取决于安装 Apache 过程中指定的 MPM 编译参数, 在 X 系统上默认的编译参数为 prefork。

由于大多数的 Unix 都不支持真正的线程,所以采用了预派生子进程 (prefork) 方式,象 Windows 或者 Solaris 这些支持 线程的平台,基于多进程多线程混合的 worker 模式是一种不错的选择。Apache 中还有一个重要的组件就是 APR(Apache portable Runtime Library),即 Apache 可移植运行库,它是一个对操作系统调用的抽象库,用来实现 Apache 内部组件对操作系统的使用,提高系统的可移植性。 Apache 对于 php 的解析,就是通过众多 Module 中的 php Module 来完成的。

Apache 生命周期

   +--------------------------------------------------------------+
   |                 +---------------------+       启动阶段        |
   |                 |    系统启动, 配置     |                      |
   |                 +----------+----------+                      |
   |                            |                                 |
   |                 +----------v----------+                      |
   |                 |      模块的初始化     |                      |
   |                 +-+--------+--------+-+                      |
   |                   |        |        |                        |
   |   +-------------+ | +------v-------+| +--------------+       |
   |   | 子进程初始化  |<+ | 子进程初始化   |+>|  子进程初始化  |       |
   |   +------+------+   +-------+------+  +-------+------+       |
   +--------------------------------------------------------------+
   |          |                  |                 |     运行阶段  |
   |     +----v----+        +----v----+       +----v----+         |
   |     | 请求循环 |        |  请求循环 |       | 请求循环 |         |
   |     +----+----+        +----+----+       +----+----+         |
   |          |                  |                 |              |
   |   +------v------+    +------v------+   +------v------+       |
   |   |  子进程结束   |    |  子进程结束  |   |   子进程结束  |       |
   |   +-------------+    +-------------+   +-------------+       |
   +--------------------------------------------------------------+

这个生命周期是在 perfork 工作下的示意,从图中可以看出,Apache 对于每一个请求都要启动一个单独的进程来处理。

Apache 的工作模式

prefork 的工作原理

一个单独的控制进程 (父进程) 负责产生子进程,这些子进程用于监听请求并作出应答。Apache 总是试图保持一些备用的 (spare) 或是空闲的子进程用于迎接即将到来的请求。这样客户端就无需在得到服务前等候子进程的产生。在 Unix 系统中,父进程通常以 root 身份运行以便邦定 80 端口,而 Apache 产生的子进程通常以一个低特权的用户运行。User 和 Group 指令用于配置子进程的低特权用户。运行子进程的用户必须要对他所服务的内容有读取的权限,但是对服务内容之外的其他资源必须拥有尽可能少的权限。

worker 的工作原理

每个进程能够拥有的线程数量是固定的。服务器会根据负载情况增加或减少进程数量。一个单独的控制进程 (父进程) 负责子进程的建立。每个子进程能够建立 ThreadsPerChild 数量的服务线程和一个监听线程,该监听线程监听接入请求并将其传递给服务线程处理和应答。Apache 总是试图维持一个备用 (spare) 或是空闲的服务线程池。这样,客户端无须等待新线程或新进程的建立即可得到处理。在 Unix 中,为了能够绑定 80 端口,父进程一般都是以 root 身份启动,随后,Apache 以较低权限的用户建立子进程和线程。User 和 Group 指令用于配置 Apache 子进程的权限。虽然子进程必须对其提供的内容拥有读权限,但应该尽可能给予他较少的特权。另外,除非使用了 suexec ,否则,这些指令配置的权限将被 CGI 脚本所继承。

Apache 的运行

启动阶段

在启动阶段,Apache 主要进行配置文件解析 (例如 http.conf 以及 Include 指令设定的配置文件等)、模块加载 (例如 mod_php.so,mod_perl.so 等) 和系统资源初始化(例如日志文件、共享内存段等)工作。在这个阶段,Apache 为了获得系统资源最大的使用权限,将以特权用户 root(X 系统)或超级管理员 administrator(Windows 系统) 完成启动。

这个过程可以通过下图来深入了解:

       +--------+      
       |  开始   |      
       +----+---+         
            |
 +----------v------------+   解析主配置文件http.conf中配置信息, 
 |     解析配置文件        |   LoadModule, AddType
 +----------+------------+   等指令被加载至内存
            |
 +----------v------------+   依据AddModule, LoadModule等指令
 |   加载静态/动态模块      |   加载Apache模块,像mod_php5.so
 +----------+------------+   加载至内存,映射到Apache地址空间。
            |
 +----------v------------+   日志文件、共享内存段,数据库链接
 |     系统资源初始化      |    等初始化
 +----------+------------+
            |
        +---v----+
        |  结束   |
        +--------+

运行阶段

在运行阶段,Apache 主要工作是处理用户的服务请求。在这个阶段,Apache 放弃特权用户级别,使用普通权限,这主要是基于安全性的考虑,防止由于代码的缺陷引起的安全漏洞。

由于 Apache 的 Hook 机制,Apache 允许模块 (包括内部模块和外部模块,例如mod_php5.so,mod_perl.so等) 将自定义的函数注入到请求处理循环中。mod_php5.so/php5apache2.dll就是将所包含的自定义函数,通过 Hook 机制注入到 Apache 中,在 Apache 处理流程的各个阶段负责处理 php 请求。

Apache 将请求处理循环分为 11 个阶段,依次是:Post-Read-Request,URI Translation,Header Parsing,Access Control,Authentication,Authorization,MIME Type Checking,FixUp,Response,Logging,CleanUp。

Apache 处理 http 请求的生命周期:

Apache处理http请求的生命周期

  1. Post-Read-Request 阶段: 在正常请求处理流程中,这是模块可以插入钩子的第一个阶段。对于那些想很早进入处理请求的模块来说,这个阶段可以被利用。
  2. URI Translation 阶段 : Apache 在本阶段的主要工作:将请求的 URL 映射到本地文件系统。模块可以在这阶段插入钩子,执行自己的映射逻辑。mod_alias 就是利用这个阶段工作的。
  3. Header Parsing 阶段 : Apache 在本阶段的主要工作:检查请求的头部。由于模块可以在请求处理流程的任何一个点上执行检查请求头部的任务,因此这个钩子很少被使用。mod_setenvif 就是利用这个阶段工作的。
  4. Access Control 阶段 : Apache 在本阶段的主要工作:根据配置文件检查是否允许访问请求的资源。Apache 的标准逻辑实现了允许和拒绝指令。mod_authz_host 就是利用这个阶段工作的。
  5. Authentication 阶段 : Apache 在本阶段的主要工作:按照配置文件设定的策略对用户进行认证,并设定用户名区域。模块可以在这阶段插入钩子,实现一个认证方法。
  6. Authorization 阶段 : Apache 在本阶段的主要工作:根据配置文件检查是否允许认证过的用户执行请求的操作。模块可以在这阶段插入钩子,实现一个用户权限管理的方法。
  7. MIME Type Checking 阶段 : Apache 在本阶段的主要工作:根据请求资源的 MIME 类型的相关规则,判定将要使用的内容处理函数。标准模块 mod_negotiation 和 mod_mime 实现了这个钩子。
  8. FixUp 阶段 : 这是一个通用的阶段,允许模块在内容生成器之前,运行任何必要的处理流程。和 Post_Read_Request 类似,这是一个能够捕获任何信息的钩子,也是最常使用的钩子。
  9. Response 阶段 : Apache 在本阶段的主要工作:生成返回客户端的内容,负责给客户端发送一个恰当的回复。这个阶段是整个处理流程的核心部分。
  10. Logging 阶段 : Apache 在本阶段的主要工作:在回复已经发送给客户端之后记录事务。模块可能修改或者替换 Apache 的标准日志记录。
  11. CleanUp 阶段 : Apache 在本阶段的主要工作:清理本次请求事务处理完成之后遗留的环境,比如文件、目录的处理或者 Socket 的关闭等等,这是 Apache 一次请求处理的最后一个阶段。

Nginx

概述

Nginx(发音同 engine x)是一款由俄罗斯程序员 Igor Sysoev 所开发轻量级的网页服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。起初是供俄国大型的门户网站及搜索引擎 Rambler(俄语:Рамблер)使用。 – 维基百科

Nginx 的模块与工作原理

Nginx 由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端请求映射到一个 location block(location 是 Nginx 配置中的一个指令,用于 URL 匹配),而在这个 location 中所配置的每个指令将会启动不同的模块去完成相应的工作。

Nginx 的模块从结构上分为核心模块、基础模块和第三方模块:

  • 核心模块:HTTP 模块、EVENT 模块和 MAIL 模块
  • 基础模块:HTTP Access 模块、HTTP FastCGI 模块、HTTP Proxy 模块和 HTTP Rewrite 模块,
  • 第三方模块:HTTP Upstream Request Hash 模块、Notice 模块和 HTTP Access Key 模块。

Nginx 的模块从功能上分为如下三类:

  • Handlers(处理器模块)。此类模块直接处理请求,并进行输出内容和修改 headers 信息等操作。Handlers 处理器模块一般只能有一个。
  • Filters (过滤器模块)。此类模块主要对其他处理器模块输出的内容进行修改操作,最后由 Nginx 输出。
  • Proxies (代理类模块)。此类模块是 Nginx 的 HTTP Upstream 之类的模块,这些模块主要与后端一些服务比如 FastCGI 等进行交互,实现服务代理和负载均衡等功能。
                     +                    ^
        Http Request |                    |  Http Response
                     |                    |
    +---------+------v-----+         +----+----+
    |  Conf   | Nginx Core |         | FilterN |
    +---------+------+-----+         +----^----+
                     |                    |
                     |               +----+----+
                     |               | Filter2 |
choose a handler     |               +----^----+
based conf           |                    |
                     |               +----+----+
                     |               | Filter1 |
                     |               +----^----+
                     |                    | Generate content
               +-----v--------------------+----+
               |           Handler             |
               +-------------------------------+

Nginx 本身做的工作实际很少,当它接到一个 HTTP 请求时,它仅仅是通过查找配置文件将此次请求映射到一个 location block,而此 location 中所配置的各个指令则会启动不同的模块去完成工作,因此模块可以看做 Nginx 真正的劳动工作者。通常一个 location 中的指令会涉及一个 handler 模块和多个 filter 模块(当然,多个 location 可以复用同一个模块)。handler 模块负责处理请求,完成响应内容的生成,而 filter 模块对响应内容进行处理。

Nginx 架构及工作流程

Nginx架构

上图是 Nginx 的架构,这个架构类似于 Apache 的 Worker 工作状态,Nginx 的每一个 Worker 进程都管理着大量的线程,真正处理请求的是 Worker 之下的线程。

所有实际上的业务处理逻辑都在 worker 进程。worker 进程中有一个函数,执行无限循环,不断处理收到的来自客户端的请求,并进行处理,直到整个 nginx 服务被停止。Worker 中这个函数执行内容如下:

  1. 操作系统提供的机制(例如 epoll, kqueue 等)产生相关的事件。
  2. 接收和处理这些事件,如是接受到数据,则产生更高层的 request 对象。
  3. 处理 request 的 header 和 body。
  4. 产生响应,并发送回客户端。
  5. 完成 request 的处理。
  6. 重新初始化定时器及其他事件。

Nginx 和 FastCGI

FastCGI

FastCGI 是一个可伸缩地、高速地在 HTTP server 和动态脚本语言间通信的接口。多数流行的 HTTP server 都支持 FastCGI,包括 Apache、Nginx 和 lighttpd 等。同时,FastCGI 也被许多脚本语言支持,其中就有 PHP。

FastCGI 是从 CGI 发展改进而来的。传统 CGI 接口方式的主要缺点是性能很差,因为每次 HTTP 服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后将结果返回给 HTTP 服务器。这在处理高并发访问时几乎是不可用的。另外传统的 CGI 接口方式安全性也很差,现在已经很少使用了。

FastCGI 接口方式采用 C/S 结构,可以将 HTTP 服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当 HTTP 服务器每次遇到动态程序时,可以将其直接交付给 FastCGI 进程来执行,然后将得到的结果返回给浏览器。这种方式可以让 HTTP 服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

Nging 和 FastCGI 合作

Nginx 不支持对外部程序的直接调用或者解析,所有的外部程序(包括 PHP)必须通过 FastCGI 接口来调用。FastCGI 接口在 Linux 下是 socket(这个 socket 可以是文件 socket,也可以是 ip socket)。

接下来以 Nginx 下 PHP 的运行过程来说明。PHP-FPM 是管理 FastCGI 的一个管理器,它作为 PHP 的插件存在。

  1. FastCGI 进程管理器 php-fpm 自身初始化,启动主进程 php-fpm 和启动 start_servers 个 CGI 子进程。主进程 php-fpm 主要是管理 fastcgi 子进程,监听 9000 端口。fastcgi 子进程等待来自 Web Server 的连接。
  2. 当客户端请求到达 Web Server Nginx 是时,Nginx 通过 location 指令,将所有以 php 为后缀的文件都交给 127.0.0.1:9000 来处理,即 Nginx 通过 location 指令,将所有以 php 为后缀的文件都交给 127.0.0.1:9000 来处理。
  3. FastCGI 进程管理器 PHP-FPM 选择并连接到一个子进程 CGI 解释器。Web server 将 CGI 环境变量和标准输入发送到 FastCGI 子进程。
  4. FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回 Web Server。当 FastCGI 子进程关闭连接时,请求便告处理完成。
  5. FastCGI 子进程接着等待并处理来自 FastCGI 进程管理器(运行在 WebServer 中)的下一个连接。

Apache 和 Nginx 比较

功能对比

Nginx 和 Apache 一样,都是 HTTP 服务器软件,在功能实现上都采用模块化结构设计,都支持通用的语言接口,如 PHP、Perl、Python 等,同时还支持正向和反向代理、虚拟主机、URL 重写、压缩传输、SSL 加密传输等。

  1. 在功能实现上,Apache 的所有模块都支持动、静态编译,而 Nginx 模块都是静态编译的,
  2. 对 FastCGI 的支持,Apache 对 Fcgi 的支持不好,而 Nginx 对 Fcgi 的支持非常好;
  3. 在处理连接方式上,Nginx 支持 epoll,而 Apache 却不支持;
  4. 在空间使用上,Nginx 安装包仅仅只有几百 K,和 Nginx 比起来 Apache 绝对是庞然大物。

Nginx 相对 apache 的优点

  • 轻量级,同样起 web 服务,比 apache 占用更少的内存及资源
  • 静态处理,Nginx 静态处理性能比 Apache 高 3 倍以上
  • 抗并发,nginx 处理请求是异步非阻塞的,而 apache 则是阻塞型的,在高并发下 nginx 能保持低资源低消耗高性能。在 Apache+PHP(prefork)模式下,如果 PHP 处理慢或者前端压力很大的情况下,很容易出现 Apache 进程数飙升,从而拒绝服务的现象。
  • 高度模块化的设计,编写模块相对简单
  • 社区活跃,各种高性能模块出品迅速啊

apache 相对 nginx 的优点

  • rewrite,比 nginx 的 rewrite 强大
  • 模块超多,基本想到的都可以找到
  • 少 bug,nginx 的 bug 相对较多
  • 超稳定
  • Apache 对 PHP 支持比较简单,Nginx 需要配合其他后端用

选择 Nginx 的优势所在

  1. 作为 Web 服务器: Nginx 处理静态文件、索引文件,自动索引的效率非常高。
  2. 作为代理服务器,Nginx 可以实现无缓存的反向代理加速,提高网站运行速度。
  3. 作为负载均衡服务器,Nginx 既可以在内部直接支持 Rails 和 PHP,也可以支持 HTTP 代理服务器对外进行服务,同时还支持简单的容错和利用算法进行负载均衡。
  4. 在性能方面,Nginx 是专门为性能优化而开发的,在实现上非常注重效率。它采用内核 Poll 模型 (epoll and kqueue),可以支持更多的并发连接,最大可以支持对 50 000 个并发连接数的响应,而且只占用很低的内存资源。
  5. 在稳定性方面,Nginx 采取了分阶段资源分配技术,使得 CPU 与内存的占用率非常低。Nginx 官方表示,Nginx 保持 10 000 个没有活动的连接,而这些连接只占用 2.5MB 内存,因此,类似 DOS 这样的攻击对 Nginx 来说基本上是没有任何作用的。
  6. 在高可用性方面,Nginx 支持热部署,启动速度特别迅速,因此可以在不间断服务的情况下,对软件版本或者配置进行升级,即使运行数月也无需重新启动,几乎可以做到 7×24 小时不间断地运行。

同时使用 Nginx 和 Apache

由于 Nginx 和 Apache 各自的优势,现在很多人选择了让两者在服务器中共存。在服务器端让 Nginx 在前,Apache 在后。由 Nginx 做负载均衡和反向代理,并且处理静态文件,讲动态请求(如 PHP 应用)交给 Apache 去处理。

参考

  1. Apache 运行机制剖析
  2. Apache 处理 http 请求的生命周期
  3. Nginx 平台初探
  4. Nginx 架构综述
  5. Apache, nginx, lighttpd 性能比较

发布者

胡中元

《中原驿站》站长

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注