CDN的核心就是一个NGINX服务器加上一堆lua写的业务代码,NGINX作为静态资源服务器非常适合用作缓存,通过简单的配置即可成为一个mini CDN。本文翻译自A Guide to Caching with NGINX and NGINX Plus。
翻译自:A Guide to Caching with NGINX and NGINX Plus
我们知道应用程序和网站的性能是成功的关键因素。然而,提升应用程序或网站性能的过程并不总是很清晰。代码质量和基础设施当然很关键,但很多情况你能通过关注一些基本的应用交付技术来极大得提升你的应用程序的用户体验。一个这种例子就是通过在你的应用程序堆栈中实现和优化缓存。这片博客涉及的技术能够帮助新手以及高级用户利用nginx中的web缓存功能来提升性能。
基础
web缓存位于客户端和“原始服务器”之间,保存它看到的内容的副本。如果一个客户端请求的内容已经存储在缓存中,缓存直接返回内容,并不和原始服务器联系。这样就提升了性能,因为web缓存服务器离客户端更近,并且省掉了每次都从头生成页面的工作所以更高效的利用应用服务器。 web浏览器和应用服务器之间可能存在多个缓存:客户端的浏览器缓存,中间缓存,内容交付网络(CDNs),和负载均衡器或位于应用服务器前面的反向代理。缓存,即使是在反向代理/负载均衡层面,也能极大得提升性能。 举个例子,去年我承担了一个任务,对一个加载缓慢的网站进行性能调优。我注意到的第一件事是首页要花1秒钟来生成。经过一番调试,我发现因为这个页面被标记为不能缓存,它对每个请求都是动态生成的。这个页面本身不会经常改变,而且不是定制化的,所以没有必要每次都重新生成。我做了一个实验,将首页标记为被负载均衡器缓存5秒,仅仅这点就带来了显著的提升。获取到第一个字节的时间下降到了几毫秒,页面的加载速度明显更快。 NGINX通常在应用程序堆栈中部署为反向代理或负载均衡器,它有一套完善的缓存功能。下一部份讨论如何在nginx中配置基本的缓存。
如何搭建和配置基本的缓存功能
要开启基本的缓存功能,只有两条指令是必须的:proxy_cache_path和proxy_cache。proxy_cache_path指令设置缓存的路径和配置,proxy_cache指令激活它。
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
server {
...
location / {
proxy_cache my_cache;
proxy_pass http://my_upstream;
}
}
最后,proxy_cache指令为匹配父级location块URL(这个例子,/)的内容激活缓存。你可以在server块中包含proxy_cache指令;它作用于该server下面不包含自己的proxy_cache指令的location块。
当源站挂掉时提供缓存的内容
NGINX内容缓存的一个强大功能是,NGINX可以被配置成当无法从源站获得最新的内容时从缓存中提供陈旧的内容。这种情况发生在被缓存资源的源站挂掉或暂时太忙。NGINX从他的缓存中提供一个陈旧的版本,而不是将错误转发给客户端。这个功能为NGINX代理的服务器提供了一种额外层面的容错能力,确保服务器故障或流量峰值的情况下正常运行。要启动这个功能,需要包含proxy_cache_use_stale指令:
location / {
...
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
}
微调缓存
NGINX有丰富的可选设置,用于微调缓存的性能。下面这个例子开启了其中几个设置:
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
server {
...
location / {
proxy_cache my_cache;
proxy_cache_revalidate on;
proxy_cache_min_uses 3;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503
http_504;
proxy_cache_lock on;
proxy_pass http://my_upstream;
}
}
将缓存拆分到多个硬盘驱动器
使用NGINX就没必要搭建一个RAID。如果有多个硬盘,NGINX可以将缓存在它们之间拆分。这个例子基于请求的URI将客户均匀得分布到两个硬盘上:
proxy_cache_path /path/to/hdd1 levels=1:2 keys_zone=my_cache_hdd1:10m
max_size=10g inactive=60m use_temp_path=off;
proxy_cache_path /path/to/hdd2 levels=1:2 keys_zone=my_cache_hdd2:10m
max_size=10g inactive=60m use_temp_path=off;
split_clients $request_uri $my_cache {
50% “my_cache_hdd1”;
50% “my_cache_hdd2”;
}
server {
...
location / {
proxy_cache $my_cache;
proxy_pass http://my_upstream;
}
}
常问问题(FAQ)
这一章节回答一些关于NGINX内容缓存常问的问题。
NGINX缓存能被装配吗?
可以的,使用add_header指令:
add_header X-Cache-Status $upstream_cache_status;
NGINX是如何决定是否缓存某些内容
NGINX默认遵守源站返回的Cache‑Control头。它不缓存Cache‑Control头被设置为Private,No‑Cache,或No‑Store或者响应头中包含Set‑Cookie的响应。NGINX只缓存GET和HEAD用户请求。你可以根据下面讲的内容来覆盖这些默认的值。 如果proxy_buffering被设置为off,NGINX不缓存任何响应,默认值是on。
Cache‑Control头可以被忽略吗?
可以的,使用proxy_ignore_headers指令。例如这个配置:
location /images/ {
proxy_cache my_cache;
proxy_ignore_headers Cache-Control;
proxy_cache_valid any 30m;
...
}
NGINX可以缓存带有Set-Cookie头的内容吗?
可以的,使用proxy_ignore_headers指令,参考上个答案中讨论的。
NGINX可以缓存POST请求吗?
可以的,使用proxy_cache_methods指令:
proxy_cache_methods GET HEAD POST;
NGINX可以缓存动态内容吗?
可以的,提供Cache-Control头就可以。将动态内容缓存很小的一段时间就能降低源站和数据库的负载,从而改善加载出第一个字节的时间,因为不需要每个请求中都重新生成页面。
我可以在我的缓存中打孔吗?
可以的,利用proxy_cache_bypass指令:
location / {
proxy_cache_bypass $cookie_nocache $arg_nocache;
...
}
NGINX使用什么cache key?
NGINX生成的默认健类似于下面NGINX变量的MD5散列值:$scheme$proxy_host$request_uri;实际使用的算法稍微更复杂一下。
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
server {
...
location / {
proxy_cache my_cache;
proxy_pass http://my_upstream;
}
}
我能将Cookie作为cache key的一部分吗?
可以的,cache key可以被配置为使用任意值,例如:
proxy_cache_key $proxy_host$request_uri$cookie_jessionid;
NGINX使用ETag头吗?
在NGINX 1.7.3盒NGINX Plus R5以及之后的版本,ETag头和IF-None_Match头被完全支持。
NGINX如何处理字节范围请求?
如果缓存中的文件是最新的,那么NGINX支持字节范围请求,只向客户端返回条目指定范围的字节。如果文件没有被缓存,或者是陈旧的,NGINX会从源站下载完整的文件。如果请求单一字节范围,当NGINX在下载完该部分就立即返回。如果请求指定了同一个文件的多个字节范围,NGINX在下载完后将整个文件返回给用户。 一旦下载完毕,NGINX将整个资源移到缓存里,将来的所有字节范围请求,无论是单一范围还是多个范围,就可以立即从缓存中获取。 请注意,要让NGINX为上游服务器提供字节范围请求,上游服务器必需要支持字节范围请求。
NGINX支持缓存清洗吗?
NGINX Plus支持有选择得清洗缓存的文件。当一个文件在源站上已经更新了单在NGINX Plus的缓存中还有效(Cache-Control:max age还有效并且proxy_cache_path指令中inactive参数设置的过期时间还没有到期)时,这个功能很有用。NGINX Plus有了cache-purge功能后,这个文件可以很容易被删除。要了解详情,请看Purging Content from the Cache。
NGINX是如何处理Pragma头的?
客户端通过添加Pragma:no‑cache头来绕过所有中间缓存,直接到达源站请求资源。NGINX默认不支持Pragma头,但是你可以使用下面的proxy_cache_bypass指令来配置这个功能:
location /images/ {
proxy_cache my_cache;
proxy_cache_bypass $http_pragma;
...
}
NGINX支持Vary头吗?
可以的,在NGINX Plus R5和NGINX 1.7.7以及更高的版本中支持。这里是一个很好的Vary头的概述。
更多阅读
有很多的方法可以定制和调优NGINX缓存。要进一步学习使用NGINX来做缓存,请参考以下资源:
- NGINX文档的ngx_http_proxy_module章节包含缓存的所有配置选项。
- 根据需求,可以访问NGINX Content Caching Webinar。该网络研讨会进一步深入本博客所展示的信息。
- NGINX Plus 管理员指南的Content Caching章节有调优NGINX缓存的更多的例子和信息。
- Content Caching with NGINX Plus产品页包含如何在NGINX Plus中配置缓存清洗的概述以及其他缓存定制的例子。