NGINX Full Version

使用 NGINX 进行微缓存的好处

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

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

 

为什么要缓存内容?

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

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

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

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

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

 

动态内容的微缓存

微缓存是一种缓存技术,只将内容缓存比如 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 秒)。为此,需要添加两条指令:

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

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 缓存中的指定内容。如果您想将内容缓存更长时间,但在有更改时即刻更新它们,就可以使用这项功能。

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