nginx 针对缓存的配置分为两个层面,一个是基于客户端的缓存机制的设置,主要利用消息头进行配置,另一个就是服务端的缓存机制,即将要缓存的内容在 nginx 服务器上进行保存,在其磁盘上存储,并根据有效时长加载在共享内存中,客户端请求 nginx 服务器后,直接从缓存内存返回。
服务端配置
nginx 服务器在接收到被代理服务器的响应数据之后,一方面将数据再返回给客户端,另一方面根据 proxy cache 的配置将这些数据缓存到本地磁盘上。当客户端再次访问相同的数据时,nginx 直接从硬盘检索到相应的数据返回给客户端,从而减少与被代理服务器交互的时间。
启用缓存
启用缓存,需在最顶层的 http 节点下使用 proxy_cache_path
指令,如下 /etc/nginx/nginx.conf
配置中:
http {
...
proxy_cache_path /tmp/nginx/my_cache keys_zone=my_cache:10m levels=1:2 inactive=60s max_size=10g;
server {
listen 80;
location / {
proxy_pass http://192.168.1.135:8080;
proxy_cache my_cache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 80s;
...
}
}
...
}
proxy_cache_path
配置项顾名思义是指定缓存的存放路径,语法如下:
语法:proxy_cache_path path keys_zone=zone_name:zone_size [levels=number] [use_temp_path=on|off] [inactive=time] [max_size=size] [min_free=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
默认值:无
位置:http
其中必选的参数具体如下:
- path:第一个参数,必选,如上 path,其指定缓存内容的本地文件系统路径;
keys_zone
:必选,指定缓存的区域 key,它在共享内存中设置一块存储区域来存放缓存的 key 字符串,如此 nginx 就可以快速判断一个 request 是否命中或者未命中缓存,其后通过冒号再指定 key 的个数,1m
表示可以存储 8000个 key,10m
可以存储 80000个 key。
常用的可选参数如下:
levels
:可选参数,指定该缓存控件对应的目录层次,最多可以设置3层,每层取值为1
或2
;多层用冒号:
隔开;
默认所有缓存文件都放在同一个目录下时,会影响缓存的性能,大部分场景推荐使用2级目录来存储缓存文件,1
和2
表示用1位和2位16进制来命名目录名称。第一级目录用1位16进制命名,如b
;第二级目录用2位16进制命名,如2b
。所以一级目录有16个,二级目录有 16*16=256个,总目录数为 16*256=4096个。
如果设置level=1:2
,那么该密文资源缓存存放在如/tmp/nginx/my_cache/e/83
目录下;
如果设置level=2:1:2
,那么该密文资源缓存存放在如/tmp/nginx/my_cache/3e/8/f8
目录下;inactive
:可选参数,指定缓存的数据多久时间未被访问就会被删除,如:inactive=1d
表示缓存数据在1天内没有被访问就会被删除;max_size
:可选参数,设置最大 cache 空间,如果不指定,会使用掉所有磁盘空间;如果缓存空间存满,会删除最少使用的缓存文件。如:max_size=20g
。
其他的可选参数如下:
use_temp_path
:可选参数,是否将缓存写在临时文件中,该参数从 1.7.10 版本开始支持;如果为off
,则 nginx 会将缓存文件直接写入指定的 cache 文件中,而不是使用 temp_path 存储,官方建议为off
,避免文件在不同文件系统中不必要的拷贝;min_free
:可选参数,最小可用空间,该参数从 1.19.1 版本开始支持;当可用空间小于该值时,nginx 会删除最近最少使用的数据,删除数据基于manager_files
、manager_threshold
和manager_sleep
三个参数;manager_files
:缓存管理器(cache manager)的可选参数,该参数从 1.11.5 版本开始支持;在一次缓存迭代(操作)中,删除不会超过该设定值,默认为 100;manager_threshold
:缓存迭代的持续时间阈值,即不要超过该值,同样,该参数从 1.11.5 版本开始支持,默认为 200 毫秒;manager_sleep
:缓存暂停的时间,同样,该参数从 1.11.5 版本开始支持,默认为 50 毫秒;loader_files
:缓存机制在启动一分钟后,“cache loader”进程被激活,它将有关存储在文件系统上的先前缓存数据的信息加载到缓存区域中。加载也是在迭代中完成的。在一次迭代中,加载的项目数不超过该值(默认情况下为 100);loader_sleep
:此外,一次迭代的持续时间受该参数限制(默认为 200 毫秒);loader_threshold
:在迭代之间,加载的暂停时间由该值限制(默认为 50 毫秒);purger
:purger 开头的是商业版支持的,都是从 1.7.12 版本开始支持,是否启用缓存清除功能,可设置on
或off
(默认);purger_files
:每次迭代清除时,清除缓存目录中缓存数据的最大文件数,默认是10
;purger_sleep
:连续两次迭代清除间的最短时间间隔,默认是50
ms;purger_threshold
:每次迭代清除时的最大执行时间,默认是50
ms。
如果是针对 uwsgi 服务的缓存,需要把上面的
proxy_cache_path
改为uwsgi_cache_path
即可,nginx 通过 ngx_http_uwsgi_module 模块实现与 uWSGI 服务器的数据交换并完成 Python 网站的请求处理。
除了在 http 节点下配置 proxy_cache_path
之外,其它还需要在具体 url 匹配配置下,进行如下主要参数的设置:
proxy_cache
:该指令用来开启或关闭代理缓存,如果开启则指定使用上面配置的哪个缓存区来进行缓存;# 语法:proxy_cache zone_name|off; # 默认:off # 位置:http、server、location
proxy_cache_key
:设置 nginx 服务器在共享内存中为缓存数据建立索引时使用的关键字,nginx 会根据该设置结合 md5 算法生成真正的缓存 key;# 语法:proxy_cache_key key; # 默认:proxy_cache_key $scheme$proxy_host$request_uri; # 位置:http、server、location
proxy_cache_key "$host$request_uri $cookie_user";
proxy_cache_key $scheme$proxy_host$uri$is_args$args;
proxy_cache_valid
:通过该参数,可以配置不同返回响应状态码的请求,生成的缓存的过期时间,可以配置多条;# 语法:proxy_cache_valid [code ...] time; # 默认:无 # 位置:http、server、location
proxy_cache_valid 200 302 10m; proxy_cache_valid 404 60s; proxy_cache_valid any 1h;
如上示例中,这里具体哪个生效,从上往下找,满足就不往下找了。
其它参数
proxy_cache_min_uses
:该指令用来设置资源被访问多少次后被缓存;# 语法:proxy_cache_min_uses number; # 默认值:1; # 位置:http、server、location
proxy_cache_methods
:该指令用户设置缓存哪些 HTTP 方法;# 语法:proxy_cache_methods GET | HEAD | POST ...; # 默认值:GET HEAD; # 位置:http、server、location # 版本支持:0.7.59 开始
proxy_cache_background_update
:使用子请求更新过去的缓存项,同时将旧的缓存返回给 client 端;# 语法:proxy_cache_background_update on | off; # 默认值:off; # 位置:http、server、location # 版本支持:1.11.10 开始
proxy_cache_bypass
:该指令是用来设置不从缓存中获取数据的条件;当字符串中有一个不为空且不等于0,就不会从缓存中获取响应;# 语法:proxy_cache_bypass string ...; # 默认值:无; # 位置:http、server、location
proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment; proxy_cache_bypass $http_pragma $http_authorization
proxy_cache_convert_head
:是否启用将 head 请求转换成 get 请求,当禁用后应将 cache_key 中包含 $request_method;# 语法:proxy_cache_convert_head on | off; # 默认值:on; # 位置:http、server、location # 版本支持:1.9.7 开始
proxy_cache_lock
:是否开启缓存的锁功能,开启后,nginx 同时只能有一个请求填充缓存中的某一数据项,不允许其他请求操作,其他请求要操作由proxy_cache_lock_timeout
配置等待时间;# 语法:proxy_cache_lock on | off; # 默认值:off; # 位置:http、server、location # 版本支持:1.1.12 开始
proxy_cache_lock_timeout
:锁后等待时间;# 语法:proxy_cache_lock_timeout time; # 默认值:5s; # 位置:http、server、location # 版本支持:1.1.12 开始
proxy_cache_lock_age
:如果一个请求在该指令设定的时间内没有完成响应数据缓存的添加,则向后端服务器再发送一次请求;# 语法:proxy_cache_lock_age time; # 默认值:5s; # 位置:http、server、location # 版本支持:1.7.8 开始
proxy_cache_max_range_offset
:设置字节请求的最大偏移量,当超过这个范围的时候请求会直接发给后端,并且不会被缓存;# 语法:proxy_cache_max_range_offset number; # 默认值:无; # 位置:http、server、location # 版本支持:1.11.6 开始
proxy_cache_revalidate
:设置在 HTTP 头中有字段属性If-Modified-Since
和If-None-Match
时是否启用重新验证;# 语法:proxy_cache_revalidate on | off; # 默认值:off; # 位置:http、server、location # 版本支持:1.5.7 开始
proxy_cache_use_stale
:当被代理服务器无法访问或出错时,nginx 用历史缓存响应客户端请求,指定具体的情况;# 语法:proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | off ...; # 默认值:off; # 位置:http、server、location
proxy_cache_purge
:定义清除缓存请求条件,若指定的字符串不为空或 0,则对proxy_cache_key
设置的标识的缓存进行清除。清除成功则返回状态码204
。仅商业版有效。# 语法:proxy_cache_purge string ...; # 默认值:无 # 位置:http、server、location # 版本支持:1.5.7 开始
完整例子
一般在调试 nginx 服务端缓存时,会使用 upstream_cache_status
变量,它存储了缓存是否命中的信息,可以设置在响应头信息中,在调试中非常有用。其变量值意义如下:
MISS
:未命中缓存;HIT
:命中缓存;EXPIRED
:缓存过期;STALE
:命中了旧的缓存;REVALIDDATED
:nginx 验证旧的缓存仍然有效;UPDATING
:内容陈旧,但正在更新;BYPASS
:X 响应从原始服务器获取。
如下是一个完整带有 nginx 服务端缓存的配置示例:
http {
...
proxy_cache_path /tmp/nginx/my_cache keys_zone=my_cache:10m levels=1:2 inactive=60s max_size=10g;
server {
listen 80;
server_name www.xxx.com
access_log /var/log/nginx/access.log;
location / {
proxy_pass http://192.168.1.135:8080;
proxy_connect_timeout 30;
proxy_read_timeout 5;
# 指定缓存区域
proxy_cache my_cache;
# 指定缓存 key 组成规则
proxy_cache_key $request_uri;
# 针对返回 200 和 302 进行 60分钟缓存
proxy_cache_valid 200 302 60m;
# 响应消息头增加 Nginx-Cache-Status 信息,用于 debug 缓存是否命中
add_header Nginx-Cache-Status $upstream_cache_status;
...
}
}
...
}
一个可用的缓存,如上四项配置(proxy_cache_path
、proxy_cache
、proxy_cache_key
、proxy_cache_valid
)即可,其余配置走默认。
客户端配置
客户端缓存指的是浏览器缓存,浏览器缓存是最快的缓存,因为它直接从本地获取(但有可能需要发送一个协商缓存的请求),它的优势是可以减少网络流量,加快请求速度。
客户端分类
浏览器缓存再分为两种模式,强缓存和协商缓存。
- 强缓存(无 HTTP 请求,无需协商):
直接读取本地缓存,无需向服务端发送请求确认,HTTP 返回状态码是 200(from memory cache 或者 from disk cache,不同浏览器返回的信息不一致的)。相关的 http 消息头 有:Cache-Control Expires
- 协商缓存(有 HTTP 请求,需协商):
浏览器虽然发现了本地有该资源的缓存,但是缓存已经过期,于是向服务器询问缓存内容是否还可以使用,若服务器认为浏览器的缓存内容还可用,那么便会返回 304(Not Modified)HTTP 状态码,告诉浏览器读取本地缓存;如果服务器认为浏览器的缓存内容已经改变,则返回新的请求的资源。相关的 http 消息头 有:Last-Modified ETag
强缓存
强制缓存原理:浏览器在加载资源的时候,会先根据本地缓存资源的 header 中的信息(Cache-Control
和 Expires
)来判断缓存是否过期。如果缓存没有过期,则会直接使用缓存中的资源;否则,会向服务端发起协商缓存的请求。
nginx 强缓存示例如下:
if ($request_uri ~* "^/$|^/search/.+/|^/company/.+/") {
add_header Cache-Control max-age=3600;
}
location ~ .*\.(css|js|swf|php|htm|html )$ {
add_header Cache-Control no-store;
}
主要消息头的具体意义如下:
Cache-Control
Cache-Control: max-age=x
客户端缓存时间超出 x 秒后则缓存过期;Cache-Control: no-cache
客户端不能直接使用本地缓存的响应,需要进行协商缓存,发送请求到服务器确认是否可以使用缓存。如果 Web 服务器返回 304,则客户端使用本地缓存,如果返回 200,则使用 Web 服务器返回的新的数据;Cache-Control: no-store
客户端不能对响应进行缓存;Cache-Control: public
可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器;Cache-Control:private
只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存;
Expires
Expires x
客户端缓存时间超出x秒后则缓存过期,优先级比Cache-Control: max-age=x
低。
协商缓存
协商缓存原理:当客户端向服务端发起请求时,服务端会检查请求中是否有对应的标识(If-Modified-Since
或 Etag
),如果没有对应的标识,服务器端会返回标识给客户端,客户端下次再次请求的时候,把该标识带过去,然后服务器端会验证该标识,如果验证通过了,则会响应 304,告诉浏览器读取缓存。如果标识没有通过,则返回请求的资源。
Last-Modified
与 If-Modified-Since
属于 HTTP/1.0,是用于服务端对响应数据修改时间进行校验的服务端校验方法。Last-Modified 的值是由服务端生成后传递给客户端的,客户端发送请求时,会将本地内容缓存中的 Last-Modified 的值由请求消息头的 If-Modified-Since 字段传递给服务端,如果服务端的被请求的内容的最后修改时间和 If-Modified-Since 的(默认是 exact 精确匹配)值不一致,则将返回新的内容,否则返回响应状态码 304,客户端将使用本地缓存。
Etag
与 If-None-Match
属于 HTTP/1.1,优先级高于 Last-Modified 的验证,是用于服务端对响应数据进行实体标签校验的服务端校验方法。Etag 类似于身份指纹,是一个可以与 Web 资源关联的记号。当客户端第一次发起请求时,Etag 的值在响应头中传递给客户端;当客户端再次发起请求时,如果验证完本地内容缓存后需要发起服务端验证,Etag 的值将由请求消息头的 If-None-Match 字段传递给服务端。如果服务端验证 If-None-Match 的值与服务端的 Etag 值不匹配,则认为请求的内容已经更新,服务端将会返回新的内容,否则返回响应状态码 304,客户端将使用本地缓存。
用户操作浏览器影响缓存
当按下 F5 或者刷新时,客户端浏览器会添加请求消息头字段 Cache-Control: max-age=0
,该请求不进行内容缓存的本地验证,会直接向 Web 服务器发起请求,服务端根据 If-Modified-Since
或者 If-None-Match
的值进行验证。
当按下 Ctrl+F5 或者强制刷新时,客户端浏览器会添加请求消息头字段 Cache-Control: no-cache
,并且忽略所有服务端验证的消息头字段(Etag
和 Last-Modified
),该请求不进行内容缓存的本地验证,它会直接向 Web 服务器发起请求,因为请求中没有携带服务端验证的消息头字段,服务端会直接返回新的内容。