缓存对于Web服务至关重要,尤其对于大型高负载Web站点。缓存作为性能优化的一个重要手段,可以在极大程度上减轻后端服务器的负载。通常对于静态资源,即不经常更新的资源,如图片,CSS或JS等进行缓存,而不用每次都向服务器请求,这样就可以减轻服务器的压力。
缓存可以分为客户端缓存
和服务端缓存
。
浏览器缓存
, 浏览器缓存是最快的缓存, 因为它直接从本地获取(但有可能需要发送一个协商缓存的请求), 它的优势是可以减少网络流量, 加快请求速度。浏览器缓存可以分为两种模式,强缓存
和协商缓存
。
强缓存(无HTTP请求,无需协商)
直接读取本地缓存,无需向服务端发送请求确认,HTTP返回状态码是200(from memory cache或者from disk cache ,不同浏览器返回的信息不一致的)。
相关的HTTP Header
有:
协商缓存(有HTTP请求,需协商)
浏览器虽然发现了本地有该资源的缓存,但是缓存已经过期,于是向服务器询问缓存内容是否还可以使用,若服务器认为浏览器的缓存内容还可用,那么便会返回304(Not Modified)HTTP状态码,告诉浏览器读取本地缓存;如果服务器认为浏览器的缓存内容已经改变,则返回新的请求的资源。
相关的HTTP Header
有:
由于网站内容的经常变化,为了保持缓存的内容与网站服务器的内容一致,客户端会通过**内容缓存的有效期(强制缓存)**以及**Web服务器提供的访问请求的校验(协商缓存)**,快速判断请求的内容是否已经更新。客户端缓存校验流程图如下:
强制缓存原理: 浏览器在加载资源的时候,会先根据本地缓存资源的header中的信息(Expires 和 Cache-Control)来判断缓存是否过期。如果缓存没有过期,则会直接使用缓存中的资源;否则,会向服务端发起协商缓存的请求。
客户端判断缓存是否过期和先前请求时服务端返回的HTTP消息头字段有关:
服务端返回字段 | 作用 |
---|---|
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 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,客户端将使用本地缓存。
下图可以看到客户端第一次请求时,客户端请求中没有If-Modified-Since和Etag标识,服务端响应了200,并且返回了Etag和Last-Modified消息头。
当第二次客户端请求时,带上了If-Modified-Since和If-None-Match消息头,并且服务端经过校验后返回了304让客户端使用本地缓存。
当按下F5或者刷新时,客户端浏览器会添加请求消息头字段Cache-Control: max-age=0,该请求不进行内容缓存的本地验证,会直接向Web服务器发起请求,服务端根据If-Modified-Since或者If-None-Match的值进行验证。
当按下Ctrl+F5或者强制刷新时,客户端浏览器会添加请求消息头字段Cache-Control: no-cache,并且忽略所有服务端验证的消息头字段(Etag和Last-Modified),该请求不进行内容缓存的本地验证,它会直接向Web服务器发起请求,因为请求中没有携带服务端验证的消息头字段,服务端会直接返回新的内容。
proxy cache属于服务端缓存,主要实现 nginx 服务器对客户端数据请求的快速响应。 nginx 服务器在接收到被代理服务器的响应数据之后,一方面将数据传递给客户端,另一方面根据proxy cache的配置将这些数据缓存到本地硬盘上。 当客户端再次访问相同的数据时,nginx服务器直接从硬盘检索到相应的数据返回给用户,从而减少与被代理服务器交互的时间。
首先需要指定proxy_cache_path,可以指定多条:
1 | proxy_cache_path /tmp/nginx/cache levels=1:2 inactive=60s keys_zone=mycache:10m max_size=10g; |
然后在http,server或者location上下文中通过proxy_cache引用前面定义的proxy_cache_path:
1 | user nginx; |
被代理服务器上需要通知代理服务器缓存内容的时间,否则代理服务器不会对内容进行缓存,通过X-Accel-Expires,expires,Cache-Control “max-age=”其中一个参数指定时间。如果代理服务器上配置了proxy_cache_valid的时间,那么被代理服务器可以不指定缓存内容的时间。
1 | events { |
设置缓存时间的字段
参数(优先级从高到低) | 位置 |
---|---|
inactive | 代理服务器 |
X-Accel-Expires | 被代理服务器 |
Cache-Control | 被代理服务器 |
expires | 被代理服务器 |
proxy_cache_valid | 代理服务器 |
客户端连续两次去访问代理服务器,可以看到第一次请求未命中缓存,第二次请求命中缓存。
1 | ❯ curl http://192.168.1.134/cache/ -I |
缓存状态
$upstream_cache_status
中包含以下几个状态:
并且在代理服务器上我们之前指定的缓存文件路径下可以看到该文件。
1 | [root@nginx-plus1 e2] ls /tmp/nginx/cache/9/e2 |
1 | user nginx; |
proxy_cache_bypass
该参数设定,什么情况下的请求不读取cache而是直接从后端的服务器上获取资源。这里的string通常为nginx的的一些内置变量或者自己定义的变量。
1 | Syntax: proxy_cache_bypass string ...; |
例如:当客户端访问请求中带有nocache或者comment参数时,不使用缓存数据。
1 | proxy_cache_bypass $arg_nocache $arg_comment; |
proxy_no_cache
该参数和proxy_cache_bypass类似,用来设定什么情况下不缓存。
1 | Syntax: proxy_no_cache string ...; |
例如:
1 | proxy_no_cache $cookie_nocache $arg_nocache $arg_comment; |
proxy_cache_key
设置遇到哪些关键字nginx服务器在共享内存中为缓存数据建立索引。
1 | Syntax: proxy_cache_key string; |
proxy_cache_methods
设置哪些HTTP请求方法,可以建立缓存。
1 | Syntax: proxy_cache_methods GET | HEAD | POST ...; |
proxy_cache_convert_head
当客户端一次使用HEAD方法请求时,nginx会通过GET方法向上游请求完整的header和body,只返回header给客户端。 当客户端下次使用GET方法请求时,nginx会把缓存好的body返回给客户端,就不用去请求上游了。
1 | Syntax: proxy_cache_convert_head on | off; |
proxy_ignore_headers
1 | Syntax: proxy_ignore_headers field ...; |
例如:
1 | proxy_ignore_headers Set-Cookie Cache-Control; |
当被代理服务器的响应存在以下头部时,nginx不会缓存:
可以设置忽略被代理服务器的响应头。
proxy_cache_valid
通过该参数,可以配置相应的http code类型的请求,生成的缓存的过期时间,可以配置多条。
1 | Syntax: proxy_cache_valid [code ...] time; |
例如:
1 | proxy_cache_valid 200 302 10m; |
proxy_cache_min_uses
当客户端请求发送的次数达到设置次数后才会缓存该请求的响应数据,如果不想缓存低频请求可以设置此项。
1 | Syntax: proxy_cache_min_uses number; |
proxy_buffering
默认是开启状态,当关闭时,nginx将不会对任何响应做缓存。
1 | Syntax: proxy_buffering on | off; |
proxy_buffers
在内存中设置缓冲区存储被代理服务器响应的body所占用的buffer个数和每个buffer大小,默认情况下buffer size等于一个memory page,32为操作系统为4k,64位为8k。当buffer大小(内存)无法容纳被代理服务器响应数据时,会将响应数据存放在proxy_temp_path中定义的临时目录(硬盘)中。
1 | Syntax: proxy_buffers number size; |
proxy_buffer_size
proxy_buffer_size 用来接受被代理服务器响应头,如果响应头超过了这个长度,nginx会报upstream sent too big header错误,然后client收到的是502。
1 | Syntax: proxy_buffer_size size; |
proxy_busy_buffers_size
nginx将会尽可能的读取被代理服务器的数据到buffer,直到proxy_buffers设置的所有buffer被写满或者数据被读取完,此时nginx开始向客户端传输数据。如果数据很大的话,nginx会接收并把他们写入到temp_file里去,大小由proxy_max_temp_file_size 控制。当数据没有完全读完的时候,nginx同时向客户端传送的buffer大小不能超过 proxy_busy_buffers_size。
1 | Syntax: proxy_busy_buffers_size size; |
proxy_temp_path
定义proxy的临时文件存在目录以及目录的层级。
1 | Syntax: proxy_temp_path path [level1 [level2 [level3]]]; |
例如:
1 | proxy_temp_path /spool/nginx/proxy_temp 1 2; |
proxy_temp_file_write_size
设置一次写入临时文件的数据的最大的大小。
1 | Syntax: proxy_temp_file_write_size size; |
proxy_max_temp_file_size
设置临时文件的最大的大小。
1 | Syntax: proxy_max_temp_file_size size; |
proxy_connect_timeout
设置和被代理服务器建立连接超时时间。
1 | Syntax: proxy_connect_timeout time; |
proxy_read_timeout
设置从被代理服务器读取响应的时间。
1 | Syntax: proxy_read_timeout time; |
proxy_send_timeout
设置发送请求给被代理服务器的超时时间。
1 | Syntax: proxy_send_timeout time; |
proxy_cache_lock
针对同一个key,仅允许一个请求回源去更新缓存,用于锁住并发回源请求。
1 | Syntax: proxy_cache_lock on | off; |
proxy_cache_lock_timeout
锁住请求的最长等待时间,超时后直接回源,但不会以此响应更新缓存。
1 | Syntax: proxy_cache_lock_timeout time; |
proxy_cache_lock_age
更新缓存的回源请求最大超时时间,超时后放行其他请求更新缓存。
1 | Syntax: proxy_cache_lock_age time; |
proxy_cache_use_stale
如果nginx在访问被代理服务器过程中出现被代理服务器无法访问或者访问出错等现象时,nginx服务器可以使用历史缓存响应客户端的请求,这些数据不一定和被代理服务器上最新的数据相一致,但对于更新频率不高的后端服务器来说,nginx服务器的该功能在一定程度上能够为客户端提供不间断访问。该指令用来设置一些状态,当被代理服务器处于这些状态时,nginx服务器启用该功能。
1 | Syntax: proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | off ...; |
例如: 配置当被代理服务器返回404 HTTP响应码时,nginx可以使用历史缓存来响应客户端。
1 | proxy_cache_use_stale http_404; |
proxy_cache_revalidate
当缓存过期时,当nginx构造上游请求时,添加If-Modified-Since和If-None-Match头部,值为过期缓存中的Last-Modified值和Etag值。
1 | Syntax: proxy_cache_revalidate on | off; |
当接收到被代理服务器的304响应时,且打开了proxy_cache_revalidate功能,则用缓存来响应客户端,并且更新缓存状态。