Nginx:高性能 Web 服务器和反向代理

作者:Will Reese

Apache 是最流行的 Web 服务器,也是有史以来最成功的开源项目之一。自 1996 年 4 月以来,Apache 服务过的网站比任何其他 Web 服务器都多。世界上许多最大的网站,包括 YouTube、Facebook、维基百科和 Craigslist,都使用 Apache 每月处理数十亿的页面浏览量。多年来,Apache 已经证明自己是一个非常稳定、安全和可配置的 Web 服务器。尽管 Apache 是一款出色的 Web 服务器,但如果有一种替代方案,它具有相同的功能、更简单的配置和更好的性能,会怎么样呢?这种 Web 服务器确实存在,它就是 Nginx。

Nginx,发音为“Engine X”,是一款高性能 Web 服务器和反向代理。它由 Igor Sysoev 为俄罗斯第二大网站 www.rambler.ru 创建。Rambler 自 2004 年夏天开始使用 Nginx,目前每天处理约 5 亿个请求。与 Apache 一样,Nginx 也被美国一些最大的网站使用,包括 WordPress (#26)、YouPorn (#27)、Hulu 和 MochiMedia。截至 2008 年 5 月,Nginx 是第四大最受欢迎的 Web 服务器,目前为超过 200 万个网站提供服务。由于它仅次于 Apache、IIS 和 GFE,因此它实际上是 Linux 可用的第二大最受欢迎的 Web 服务器。

为什么使用 Nginx?

与 Apache 一样,Nginx 具有您期望从领先的 Web 服务器获得的所有功能

  • 静态文件服务。

  • SSL/TLS 支持。

  • 虚拟主机。

  • 反向代理。

  • 负载均衡。

  • 压缩。

  • 访问控制。

  • URL 重写。

  • 自定义日志记录。

  • 服务器端包含。

  • WebDAV。

  • FLV 流媒体。

  • FastCGI。

它稳定、安全且非常易于配置,正如您将在本文后面看到的那样。但是,Nginx 相对于 Apache 的主要优势在于性能和效率。

我使用 ab(Apache 的基准测试工具)对 Nginx v0.5.22 和 Apache v2.2.8 进行了简单的测试。在测试期间,我使用 vmstat 和 top 监控了系统。结果表明,在服务静态内容时,Nginx 的性能优于 Apache。两个服务器在并发数为 100 时表现最佳。Apache 使用了四个工作进程(线程模式),30% 的 CPU 和 17MB 的内存,每秒处理 6,500 个请求。Nginx 使用了一个工作进程,15% 的 CPU 和 1MB 的内存,每秒处理 11,500 个请求。

Nginx 能够以更少的资源每秒处理更多请求,这归功于其架构。它由一个主进程组成,该主进程将工作委托给一个或多个工作进程。每个工作进程使用来自 Linux 内核(epoll/select/poll)的特殊功能,以事件驱动或异步方式处理多个请求。这使 Nginx 能够以极小的开销快速处理大量并发请求。Apache 可以配置为每个请求使用一个进程(pre-fork)或为每个请求使用一个线程(worker)。尽管 Apache 的线程模式比其 pre-fork 模式性能好得多,但它仍然比 Nginx 的事件驱动架构使用更多的内存和 CPU。

安装和基本用法

Nginx 在大多数 Linux 发行版中都可用。在本文中,我使用 Ubuntu 8.04 (Hardy),它包含 Nginx 版本 0.5.33。如果您的发行版没有 Nginx,或者您想运行更新的版本,您可以随时下载最新的稳定版本(在撰写本文时为 v0.6.31)并从源代码安装。

以 root 身份运行以下命令来安装 Nginx

# apt-get install nginx

现在 Nginx 已经安装完成,您可以使用启动脚本来启动、停止或重启 Web 服务器

# /etc/init.d/nginx start
# /etc/init.d/nginx stop
# /etc/init.d/nginx restart 

大多数配置更改不需要重启,在这种情况下,您可以使用 reload 命令。通常,在重新加载之前测试 Nginx 配置文件是否有错误是一个好主意

# nginx -t
# /etc/init.d/nginx reload

让我们继续启动服务器

# /etc/init.d/nginx start

现在 Nginx 应该在您的机器上运行了。如果您在浏览器中打开 http://127.0.0.1/,您应该看到一个带有“Welcome to nginx!”的页面。

主要配置文件

现在 Nginx 已经安装完成,让我们看一下它的配置文件,位于 /etc/nginx/nginx.conf。此文件包含 Nginx 的服务器范围设置,它应该看起来类似于这样

user www-data;
worker_processes  1;
error_log  /var/log/nginx/error.log;
pid  /var/run/nginx.pid;
events {
  worker_connections  1024;
}
http {
  include  /etc/nginx/mime.types;
  default_type  application/octet-stream;
  access_log  /var/log/nginx/access.log;
  sendfile  on;
  keepalive_timeout  65;
  tcp_nodelay  on;
  gzip  on;
  include /etc/nginx/sites-enabled/*;
}

我们不会更改任何这些设置,但让我们讨论其中一些设置,以帮助我们了解 Nginx 的工作原理。worker_processes 设置告诉 Nginx 启动多少个子进程。如果您的服务器有多个处理器或正在执行大量的磁盘 IO,您可能需要尝试增加此数字,看看是否能获得更好的性能。worker_connections 设置限制每个工作进程的并发连接数。要确定最大并发请求数,您只需将 worker_processes 乘以 worker_connections。

error_log 和 access_log 设置指示默认的日志记录位置。您也可以在每个站点的基础上配置这些设置,正如您将在本文后面看到的那样。与 Apache 一样,Nginx 配置为以 www-data 用户身份运行,但您可以使用 user 设置轻松更改此设置。Nginx 的启动脚本需要知道主进程的进程 ID,该 ID 存储在 /var/run/nginx.pid 中,如 pid 设置所示。

sendfile 设置允许 Nginx 使用特殊的 Linux 系统调用,以非常有效的方式通过网络发送文件。gzip 选项指示 Nginx 压缩每个响应,这会占用更多 CPU,但可以节省带宽并缩短响应时间。此外,Nginx 还提供了另一个压缩模块,称为 gzip 预压缩(从 0.6.24 版本开始可用)。此模块在同一位置查找扩展名为 .gz 的文件的压缩副本,并将其提供给启用 gzip 的客户端。这避免了每次请求时都必须压缩文件。

我们关心的最后一个设置是 sites-enabled 目录的 include 指令。在 /etc/nginx 内部,您会看到另外两个目录,/etc/nginx/sites-available 和 /etc/nginx/sites-enabled。对于您想要使用 Nginx 托管的每个网站,您应该在 /etc/nginx/sites-available 中创建一个配置文件,然后在 /etc/nginx/sites-enabled 中创建一个指向您创建的配置文件的符号链接。主要的 Nginx 配置文件包含 /etc/nginx/sites-enabled 中的所有文件。这有助于组织您的配置文件,并使启用和禁用特定网站变得非常容易。

静态 Web 服务器

现在我们已经介绍了主要的配置文件,让我们为一个基本的网站创建一个配置文件。在我们开始之前,我们需要禁用 Ubuntu 为我们创建的默认站点

# rm -f /etc/nginx/sites-enabled/default

现在,创建一个名为 /etc/nginx/sites-available/basic 的新配置文件,内容如下

server {
  listen  127.0.0.1:80;
  server_name  basic;
  access_log  /var/log/nginx/basic.access.log;
  error_log  /var/log/nginx/basic.error.log;
  location  / {
    root  /var/www/basic;
    index  index.html index.htm;
  }
}

创建根目录和 index.html 文件

# mkdir /var/www/basic
# cd /var/www/basic
# echo "Basic Web Site" > index.html

启用站点并重启 Nginx

# cd /etc/nginx/sites-enabled
# ln -s ../sites-available/basic . 
# /etc/init.d/nginx restart

如果您在浏览器中打开 http://127.0.0.1/,您应该看到一个带有“Basic Web Site”的页面。正如您所看到的,使用 Nginx 创建新站点非常容易。

让我们回顾一下我们创建的新配置文件。server 指令用于定义一个新的虚拟服务器,其所有设置都用大括号括起来。listen 指令指示此服务器将接受请求的 IP 和端口,server_name 设置虚拟服务器的主机名。正如我之前提到的,access_log 和 error_log 设置可以在每个站点的基础上设置。通常,为每个站点提供自己的一组日志文件是一个好主意。

接下来是 location 指令,它允许您修改站点不同部分的设置。在我们的例子中,我们只有一个用于整个站点的位置。但是,您可以有多个 location 指令,并且可以使用正则表达式来定义它们。在我们的 location 块内,我们还有两个其他指令:root 和 index。root 指令用于定义此位置的文档根目录。这意味着对 /img/test.gif 的请求将在 /var/www/localhost/img/test.gif 中查找文件。最后,index 指令告诉 Nginx 将哪些文件用作此位置的默认文件。

带有 SSL 的静态 Web 服务器

一些网站,例如在线商店,需要安全通信 (HTTPS) 来保护信用卡交易和客户信息。与 Apache 一样,Nginx 通过 SSL 模块支持 HTTPS,并且非常容易设置。

首先,您需要生成 SSL 证书。openssl 命令会询问您一堆问题,但您可以简单地为每个问题按 Enter 键

# apt-get install openssl
# mkdir /etc/nginx/ssl
# cd /etc/nginx/ssl
# openssl req -new -x509 -nodes -out server.crt -keyout server.key

创建一个名为 /etc/nginx/sites-available/secure 的新配置文件,其中包含以下内容

server {
  listen   127.0.0.1:443;
  server_name  secure;
  access_log  /var/log/nginx/secure.access.log;
  error_log  /var/log/nginx/secure.error.log;
  ssl on;
  ssl_certificate /etc/nginx/ssl/server.crt;  
  ssl_certificate_key /etc/nginx/ssl/server.key;  
  location / {
    root   /var/www/secure;
    index  index.html index.htm;
  }
}

创建根目录和 index.html 文件

# mkdir /var/www/secure
# cd /var/www/secure
# echo "Secure Web Site" > index.html

启用站点并重启 Nginx

# cd /etc/nginx/sites-enabled
# ln -s ../sites-available/secure . 
# /etc/init.d/nginx restart

如果您在浏览器中打开 https://127.0.0.1/(注意 https),您可能会收到一条关于无法验证证书的警告。那是因为我们在这个例子中使用了自签名证书。继续并告诉您的浏览器接受证书,您应该看到一个带有“Secure Web Site”的页面。

此配置文件与我们之前的配置非常相似,但有一些不同之处。首先,请注意这个新服务器正在监听端口 443,这是 HTTPS 的标准端口。其次,我们使用以下行启用了 SSL 模块ssl on;。如果您自己编译 Nginx 而不是使用 Ubuntu 包,您需要确保您指定了--with-http_ssl_module当您运行./configure; 否则,SSL 模块将不可用。第三,我们使用 ssl_certificate 和 ssl_certificate_key 指令指向我们之前创建的证书和密钥。

虚拟主机

在许多情况下,您会希望从单个服务器运行多个网站。这称为虚拟主机,Nginx 支持基于 IP 和基于名称的虚拟主机。

让我们创建两个虚拟主机:one.example.com 和 two.example.com。首先,我们需要在我们的 /etc/hosts 文件中添加一行,以便 one.example.com 和 two.example.com 指向我们的服务器(通常您会使用 DNS 执行此操作)

# echo "127.0.0.1 one.example.com two.example.com" >> /etc/hosts

现在,我们需要为每个站点创建一个配置文件。首先,创建一个名为 /etc/nginx/sites-available/one 的文件,内容如下

server {
  listen   127.0.0.1:80;
  server_name  one.example.com;
  access_log  /var/log/nginx/one.access.log;
  error_log /var/log/nginx/one.error.log;
  location / {
    root   /var/www/one;
    index  index.html index.htm;
  }
}

然后,复制该文件并命名为 /etc/nginx/sites-available/two,并将每个出现的“one”替换为“two”

# cd /etc/nginx/sites-available
# cp one two
# sed -i "s/one/two/" two

创建根目录和 index.html 文件

# mkdir /var/www/{one,two}
# echo "Site 1" > /var/www/one/index.html
# echo "Site 2" > /var/www/two/index.html

启用站点并重启 Nginx

# cd /etc/nginx/sites-enabled
# ln -s ../sites-available/one .
# ln -s ../sites-available/two .
# /etc/init.d/nginx restart

如果您在浏览器中打开 http://one.example.com/,您应该看到一个带有“Site 1”的页面。对于 http://two.example.com/,您应该看到“Site 2”。

我们刚刚通过更改 server_name 指令创建了两个在 127.0.0.1 上运行的基于名称的虚拟主机。对于基于 IP 的虚拟主机,只需更改 listen 指令,为每个站点使用不同的 IP。

现在,继续禁用这两个虚拟主机

# rm -f /etc/nginx/sites-enabled/one
# rm -f /etc/nginx/sites-enabled/two
# /etc/init.d/nginx restart

当您完成操作后,不要忘记删除我们添加到 /etc/hosts 的行。

反向代理和负载均衡器

除了是一个极其快速的静态 Web 服务器之外,Nginx 还是一个负载均衡器和反向代理。负载均衡器是一种用于将工作分散到多个服务器或进程的设备,而反向代理是一个透明地将请求移交给另一个服务器的服务器。除其他事项外,这允许 Nginx 处理静态内容的请求,并将动态内容的请求负载均衡到许多不同的后端服务器或进程。

对于此示例,让我们创建一个非常简单的 Python Web 服务器来提供一些动态内容。如果您不熟悉 Python,请不要担心;我们只是使用它来显示一个 Web 页面,指示服务器正在哪个端口上运行。将以下内容保存到一个名为 /tmp/server.py 的文件中

import sys,BaseHTTPServer as B
class Handler(B.BaseHTTPRequestHandler):
  def do_GET(self):
    self.wfile.write("Served from port %s" % port)
  def log_message(self, *args):
    pass
if __name__ == '__main__':
  host,port = sys.argv[1:3]
  server = B.HTTPServer((host,int(port)), Handler)
  server.serve_forever()

现在我们可以启动两个本地服务器,每个服务器在不同的端口上

# python /tmp/server.py 127.0.0.1 8001 &
# python /tmp/server.py 127.0.0.1 8002 &

如果您在浏览器中打开 http://127.0.0.1:8001/,您应该看到“Served from port 8001”,如果您打开 http://127.0.0.1:8002/,您应该看到“Served from port 8002”。

现在,创建一个名为 /etc/nginx/sites-available/proxy 的新配置文件,其中包含以下内容

upstream python_servers {
  server 127.0.0.1:8001;
  server 127.0.0.1:8002;
}
server {
  listen   127.0.0.1:8000;
  server_name  proxy;
  access_log  /var/log/nginx/proxy.access.log;
  error_log /var/log/nginx/proxy.error.log;
  location / {
    proxy_pass http://python_servers;
  }
}

启用站点并重启 Nginx

# cd /etc/nginx/sites-enabled
# ln -s ../sites-available/proxy .
# /etc/init.d/nginx restart

如果您在浏览器中打开 http://127.0.0.1:8000/,您应该看到一个带有“Served from port 8001”或“Served from port 8002”的页面,并且每次刷新页面时都应该交替显示。

让我们回顾一下其中一些新设置。upstream 块定义了一组后端服务器的名称。在我们的例子中,我们定义了一个名为 python_servers 的组,其中包含我们在端口 8001 和 8002 上启动的两个本地 Python 服务器。然后,我们将 Nginx 配置为使用以下行将所有请求移交给我们的后端服务器proxy_pass http://python_servers;。Nginx 以轮询方式自动负载均衡到每个 Python 服务器的请求。您还可以为每个后端设置权重,以便您可以将更多或更少的请求定向到特定服务器。

Nginx 自动处理后端故障,并将停止向失败的后端服务器发送请求,直到它再次开始响应。为了演示这一点,我们可以杀死在端口 8001 上运行的 Python 进程。使用 jobs 命令查找在端口 8001 上运行的 Python 进程的作业号,并使用kill %<作业号>杀死进程

# jobs
# kill %1

在浏览器中打开 http://127.0.0.1:8000/ 并不断刷新页面,您应该只看到“Served from port 8002”页面。Nginx 检测到端口 8001 的后端服务器没有响应,因此它停止向该服务器发送请求。现在,重启端口 8001 的 Python 进程

# python /tmp/server.py 127.0.0.1 8001 &

继续刷新页面,您应该看到您的浏览器再次开始在“Served from port 8001”和“Served from port 8002”之间交替显示。Nginx 检测到端口 8001 后端正在响应,并开始向其发送请求。

结论

无论您是想充分利用您的 VPS,还是试图扩展世界上最大的网站之一,Nginx 都可能是这项工作的最佳工具。它快速、稳定且易于使用。感谢 Igor Sysoev 创建了这个优秀的软件。

Will Reese 在过去的十年中一直从事 Linux 工作,主要负责扩展运行在 Apache、Python 和 PostgreSQL 上的 Web 应用程序。他喜欢在桌上足球和 Wii 网球中击败 Cory Wright。

加载 Disqus 评论