NGINX.COM
Web Server Load Balancing with NGINX Plus

NGINX 和 NGINX Plus 被广泛应用于 Web 内容缓存,小到个人网站,大到一些世界大型内容交付网络 (CDN),例如 MaxCDN 和 CloudFlare。

微缓存通过将动态、非个性化内容缓存很短的时间,可以有效地加速这些内容的交付。在本文中,我们将展示如何借助微缓存技术将基于 WordPress 的应用提速高达 400 倍。

 

为什么要缓存内容?

缓存能够一举两得:通过更快地交付内容改善网站性能,同时减轻源服务器的负担。缓存的效率取决于内容的可缓存性。这些内容可以存储多长时间,如何检查更新,可以将同一缓存内容发送给多少用户?

缓存静态内容,例如图片、JavaScript 和 CSS 文件以及几乎不变的 Web 内容,是个相对简单的过程。缓存更新的处理方法包括常规过期条件 get,必要时,还可以使用 cachebusting 技术来更改引用对象的 URL。

通常无法缓存个性化内容(即服务器应用为每位用户定制提供的内容),因为针对对同一资源的每次请求,服务器的响应不尽相同。服务器端引用 (SSI) 和页面片段缓存 (ESI) 等技术可用于组装页面,但是这些技术实施起来比较复杂,而且不一定能改善性能。

在以上两类内容中间,是另一种有趣的待缓存对象,即可能会发生意外更改,但并未针对每位用户进行个性化(或者在客户端使用 JavaScript 进行个性化)的动态内容。这类内容的生成开销较高,提供过时版本会引发一系列问题。

适合缓存的动态内容包括:

  • 繁忙新闻或博客网站的首页,每隔几秒就有新文章发布
  • 最近资讯的 RSS 源
  • 持续集成 (CI) 或构建平台的状态页面
  • 库存、状态或筹款计数
  • 彩票结果
  • 日历数据
  • 在客户端设备上生成的个性化动态内容,例如使用 cookie 数据生成的广告内容或数据(“你好,(你的名字)”)

 

动态内容的微缓存

微缓存是一种缓存技术,只将内容缓存比如 1 秒左右的极短时间。这实际上意味着网站更新延迟不到 1 秒钟,这在很多情况下都是可以接受的。

这种短暂缓存能给网站性能带来明显的改观吗?让我们试试看!

测试应用

在本次测试中,我们将使用标准 WordPress install,并为其填充一些样本内容

显而易见,即便是处理基本内容,WordPress 服务器也存在性能问题:使用 ab 进行基准测试时,它每秒只能服务 5.53 个请求:

root@nginx-client:~# >ab -c 10 -t 30 -k http://nginx-server/
Requests per second:    5.53 [#/sec] (mean)
Time per request:       1809.260 [ms] (mean)
Time per request:       180.926 [ms] (mean, across all concurrent requests)
Transfer rate:          319.74 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       3
Processing:  1430 1735 259.4   1580    2228
Waiting:      537  683 119.7    624     980
Total:       1430 1735 259.4   1580    2228

在测试期间,vmstat 显示造成瓶颈的主要原因是利用 PHP 生成页面的 CPU 消耗(在 cpu 下面的 us 一列,数值为 96 到 98):

root@nginx-server:/var/www/html# vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
10  0      0 136076  44944 585920    0    0     0     0  476 1665 96  4  0  0  0
10  0      0 140112  44952 585924    0    0     0     4  506 1773 98  2  0  0  0
10  0      0 136208  44952 585924    0    0     0     0  576 2057 97  3  0  0  0

top 命令显示,CPU 被 10 个执行 PHP 解释器的 Apache httpd 进程占用。

这种设置本身就存在问题——它限制网站每秒处理请求数不能超过 5 个,导致网站极易遭到 DOS 攻击,而通过添加 CPU 来解决这个问题则意味着每年托管费用增加 1,000 美元。

利用 NGINX 部署简单微缓存

利用 NGINX 来加速该服务只需两步。

第一步:通过 NGINX 进行代理

在 WordPress 服务器上安装 NGINX 或 NGINX Plus,并将其配置为接收入站流量并在内部转发给 WordPress 服务器:

NGINX 代理配置相对简单:

server {
    listen external-ip:80;  # External IP address

    location / {
        proxy_http_version 1.1; # Always upgrade to HTTP/1.1 
        proxy_set_header Connection ""; # Enable keepalives
        proxy_set_header Accept-Encoding ""; # Optimize encoding
        proxy_pass http://wordpress-upstreams;
    }

    status_zone wordpress; # NGINX Plus status monitoring
}

upstream wordpress-upstreams {
    zone wordpress 128k;
    keepalive 20; # Keepalive pool to upstream

    server localhost:80;
}

我们还修改了 Apache 配置(监听端口号和虚拟服务器),以便 Apache 仅绑定到 localhost:80。

您可能会认为,添加额外的代理服务器会对性能产生不利影响,但实际上由此造成的性能影响可忽略不计:

root@nginx-client:~# ab -c 10 -t 30 -k http://nginx-server/
Requests per second:    5.63 [#/sec] (mean)
Time per request:       1774.708 [ms] (mean)
Time per request:       177.471 [ms] (mean, across all concurrent requests)
Transfer rate:          324.44 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       1
Processing:  1423 1709 341.3   1532    2794
Waiting:      554  703 165.0    608    1165
Total:       1423 1709 341.4   1532    2794

在较繁忙的服务器(处理更多并发请求)中,仅靠 NGINX 实现的 keepalive 优化就能带来显著的性能提升

第二步:启用短期缓存

只需在服务器配置中添加两条指令,NGINX 或 NGINX Plus 就可以缓存所有可缓存的响应。带有 200 OK 状态码的响应只缓存 1 秒。

proxy_cache_path /tmp/cache keys_zone=cache:10m levels=1:2 inactive=600s max_size=100m;

server {
    proxy_cache cache;
    proxy_cache_valid 200 1s;
    # ...
}

当我们再次运行基准测试时,可以看到显著的性能提升:

root@nginx-client:~# ab -c 10 -t 30 -k http://nginx-server/
Complete requests:      18022
Requests per second:    600.73 [#/sec] (mean)
Time per request:       16.646 [ms] (mean)
Time per request:       1.665 [ms] (mean, across all concurrent requests)
Transfer rate:          33374.96 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.5      1      10
Processing:     0   16 141.5      3    2119
Waiting:        0    6  54.6      1     818
Total:          1   17 141.5      4    2121

性能提升 120 倍,每秒处理请求数从 5 条增加到 600 条;这简直太棒了,但还有个问题。

虽然缓存运行平稳,每秒刷新一次内容(因此永不过时),但是计划之外的情况发生了。大家可以看到处理时间的标准偏差很大(141.5 毫秒)。CPU 使用率仍为 100%(用 vmstat 测量),top 显示有 10 个活跃的 httpd 进程。

我们可以从 NGINX Plus 的实时活动监控仪表盘中获得进一步的线索。测试前:

测试后:

仪表盘报告显示,NGINX 在测试期间处理了 18,032 条请求(ab 报告的 18,022 条请求,加上基准测试在 30 秒结束时未处理的 10 条请求)。但是,NGINX 将 150 条请求转发给了上游服务器,这远超我们的预期(在 30 秒测试期间内将内容缓存 1 秒钟)。

怎么回事?为什么 CPU 使用率很高,缓存更新次数超出预期?

这是因为每当缓存条目过期,NGINX 就会停止使用它。NGINX 将所有请求都转发给上游 WordPress 服务器,直到它收到响应,可以用新内容重新填充缓存。

这导致 WordPress 服务器收到的请求经常激增到 10 条。这些请求不光占用 CPU,而且比缓存响应的请求延迟更大,这就造成了测试结果中的高标准差。

使用 NGINX 优化微缓存

我们的策略很清晰:需要在确保缓存内容最新的情况下,尽可能少地向上游源服务器转发请求。在缓存内容进行更新时,可以从缓存中提供旧的响应(延后 1 到 2 秒)。为此,需要添加两条指令:

  • proxy_cache_lock——限制填充缓存的并发尝试数,这样当一条缓存项创建后,对该资源的后续请求将会在 NGINX 中排队。
  • proxy_cache_use_stale——将 NGINX 配置为在缓存项进行更新时提供旧(最近缓存)内容。

加上之前添加的缓存指令,可得到如下服务器配置:

server {
    proxy_cache one;
    proxy_cache_lock on;
    proxy_cache_valid 200 1s;
    proxy_cache_use_stale updating;
    # ...
}

基准测试结果变化显著。每秒请求数从 600 跃升至 2,200 左右:

root@nginx-client:~# ab -c 10 -t 30 -n 100000 -k http://nginx-server/
Concurrency Level:      10
Time taken for tests:   30.001 seconds
Complete requests:      65553
Failed requests:        0
Keep-Alive requests:    0
Total transferred:      3728905623 bytes
HTML transferred:       3712974057 bytes
Requests per second:    2185.03 [#/sec] (mean)
Time per request:       4.577 [ms] (mean)
Time per request:       0.458 [ms] (mean, across all concurrent requests)
Transfer rate:          121379.72 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.3      1       5
Processing:     1    4   8.1      3     661
Waiting:        0    1   2.6      1     250
Total:          1    5   8.1      4     661

CPU 使用率也低很多(注意 cpu 下面 id 一栏的空闲时间):

root@nginx-server:/var/www/html# vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system--- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs  us sy id wa st
 1  0      0 106512  53192 641116    0    0     0    37 11016 3727 19 45 36  0  0
 1  0      0 105832  53192 641808    0    0     0    68 17116 3521 13 56 31  0  0
 1  0      0 104624  53192 643132    0    0     0    64 14120 4487 15 51 33  0  0

数据传输率(121379.72 千字节/秒,或 121 兆字节/秒)相当于 0.97 Gbps,因此该测试受网络限制。CPU 平均使用率为 66%,该服务器的峰值性能大概为  2185/0.66 = 3300 个请求/秒。

另外请注意 ab 报告的连续响应时间(标准偏差只有 8.1 毫秒),以及仪表盘显示的在 30 秒测试期间转发给上游服务器的请求数量 (16):

为什么只有 16 条请求?我们知道缓存到 1 秒钟时会超时,更新过程需要长达 0.661 秒(从 ab 结果来看),因此可以推测,更新频率不会快于每 1.66 秒一次。在 30 秒钟的时间内,预计收到最多 18 (30/1.66) 条请求。

 

了解更多信息

本文简单展示了将动态内容缓存较短时间的潜在好处,以及 NGINX Plus 的实时活动监测数据在缓存配置调优和诊断中的用处。如果您想在生产环境中使用微缓存,建议您创建并测试一个更为复杂的缓存策略,对动态和静态内容进行更长时间的微缓存。

NGINX Plus 还拥有一项缓存清除功能,可用来快速清除 NGINX 缓存中的指定内容。如果您想将内容缓存更长时间,但在有更改时即刻更新它们,就可以使用这项功能。

如欲了解更多信息,请查阅以下资源:

Hero image
免费 O'Reilly 电子书:
《NGINX 完全指南》

更新于 2022 年,一本书了解关于 NGINX 的一切

关于作者

Owen Garrett

产品管理高级总监

Owen is a senior member of the NGINX Product Management team, covering open source and commercial NGINX products. He holds a particular responsibility for microservices and Kubernetes‑centric solutions. He’s constantly amazed by the ingenuity of NGINX users and still learns of new ways to use NGINX with every discussion.

关于 F5 NGINX

F5, Inc. 是备受欢迎的开源软件 NGINX 背后的商业公司。我们为现代应用的开发和交付提供一整套技术。我们的联合解决方案弥合了 NetOps 和 DevOps 之间的横沟,提供从代码到用户的多云应用服务。访问 nginx-cn.net 了解更多相关信息。