Nginx缓存服务应用实战
向用户提供内容服务是网站的核心目的,用户能够通过浏览器或客户端快速打开网站是十分重要的。而用户在通过客户端从网站上获取浏览内容的这一路径上,要经历很多复杂的过程,如路径的解析、数据的产生及返回等。同样,提升网站性能的方法也是复杂多样的,而代码质量和应用架构至关重要。在很多情况下,通过使用缓存技术优化应用架构中的交付技术可加快用户对网站内容的获取,以提升用户体验。网站架设者们会在可能影响数据传输速度的各个环节中使用缓存技术,如浏览器缓存、内容分发网络(Content Delivery Network, CDN)、反向代理缓存等。反向代理缓存技术是位于用户和网站应用服务器之间的一种加速技术,其按需保存了所有经其转发给客户端的内容,当用户请求的内容已经存在于缓存中时,该内容将立即被返回给用户,这时并不需要与网站应用服务器通信,极大地提高了请求响应速度。反向代理缓存技术是目前使用最为广泛的内容加速技术。由于互联网的庞大规模和基础设备的复杂性,反向代理缓存技术也被以"CDN产品"的形式部署得更接近终端用户。在使用CDN技术后,终端用户可无须考虑时间、地点、运营商等因素而快速打开网站。
Nginx缓存服务的重要应用就是反向代理缓存服务,是其基于Nginx的代理功能,它不仅可以更高效地提升应用服务的性能,提高Web网站的可用性。当应用服务器繁忙或出现故障时,Nginx缓存服务可以将错误返回的内容重新定向到早已准备好的静态缓存中,它不仅可以获得更好的用户体验,还能为其定位及排查故障提供充裕的时间。
1、Web缓存
Web缓存可节约网络带宽,有效提高用户打开网站的速度。由于应用服务器被请求次数的降低,也相对使它的稳定性得到了提升。Web缓存从数据内容传输的方向分为前向位置缓存和反向位置缓存两类。如图所示,前向位置缓存既可以是用户的客户端浏览器,也可以是位于用户ISP或内部局域网的服务器。反向位置缓存通常位于互联网端,如内容分发网络或网站的反向代理缓存等。

1.1、客户端缓存
当客户端访问某一网站时,通常会多次访问同一页面,如果每次都到网站服务器获取相同的内容,不仅会造成用户自身网络资源的浪费,也会加重网站服务器的负载。为了提高访问效率,客户端浏览器会将访问的内容在本地生成内容缓存。由于网站的内容经常变化,为了保持缓存的内容与网站服务器的内容一致,客户端会通过内容缓存的有效期及Web服务器提供的访问请求校验机制,快速判断请求的内容是否已经更新。客户端缓存校验流程如图所示。

客户端通过内容缓存有效期的本地校验和由Web服务端提供的服务端校验两种方式共同校验内容缓存是否有效,这两种方式都是通过HTTP消息头中的相应字段进行判断或与服务端交互的。HTTP消息头字段功能说明如表所示。

- 1)消息头字段Cache-Control由客户端发起缓存控制的相关字段值如表所示。

- 当按下F5或者点击刷新时,客户端浏览器会添加请求消息头字段Cache-Control: max-age=0,该请求不进行内容缓存的本地验证,会直接向Web服务端发起请求,服务端将根据消息头字段进行服务端验证。
- 当按下Ctrl+F5时,客户端浏览器会添加请求消息头字段Cache-Control: no-cache和Pragma: no-cache,并忽略所有服务端验证的消息头字段,该请求不进行内容缓存的本地验证,它会直接向Web服务端发起请求,因没有服务端验证的消息头字段,所以会直接返回请求内容。
- 2)消息头字段Cache-Control由服务端发起缓存控制的相关字段值如表所示。

- 3)Last-Modified与if-modified-since属于HTTP/1.0,是用于服务端对响应数据修改时间进行校验的服务端校验方法。Last-Modified的值是由服务端生成后传递给客户端的,客户端发送请求时,它会将本地内容缓存中的Last-Modified的值由请求消息头的if-modified-since字段传递给服务端,如果服务端被请求的内容在if-modified-since字段值的时间之后被修改了,将返回被修改的内容,否则返回响应状态码304,客户端将使用本地缓存。
- 4)ETag与If-None-Match属于HTTP/1.1,优先级高于Last-Modified的验证,是用于服务端对响应数据进行实体标签校验的服务端校验方法。ETag类似于身份指纹,是一个可以与Web资源关联的记号(token)。当客户端第一次发起请求时,ETag的值在响应消息头中传递给客户端;当客户端再次发送请求时,如果验证本地内容缓存需要发起服务端验证,Etag的值将由请求消息头的If-None-Match字段传递给服务端。如果验证本地内容缓存与服务端的ETag的匹配不一致,则认为请求的内容已经更新,服务端将返回新的内容,否则返回响应状态码304,客户端将使用本地缓存。
- 5)客户端会通过HTTP消息头字段对本地内容缓存进行本地校验和服务端校验,内容缓存校验的流程如图所示。

1.2、正向代理缓存
当客户端浏览器通过正向代理缓存服务器访问互联网Web服务器时,正向代理缓存服务器会先检查本地的缓存,如果本地已经有客户端访问网站的内容缓存,则会根据缓存策略将缓存内容返回客户端;如果本地没有相应的内容缓存,则会向网站Web服务器发起访问请求,在获得响应数据后,它会先将响应内容在本地缓存生成内容缓存,然后再转发给客户端。正向代理缓存架构如图所示。

- 通常是多个客户端共享一台正向代理缓存服务器,当一台客户端访问某个网站后,其他客户端均会共享这个网站的缓存,无须再向网站服务器发起访问请求,提升内容响应速度。
- 通过共享正向代理缓存服务器,不仅减少了外网的访问次数,也降低了网络带宽的需求。
- 通过正向代理缓存服务器的控制策略,可以有效地针对内网客户端及访问的目标进行过滤控制,提升内网安全。
正向代理缓存服务器并不严格限制其一定要在客户端的内网,因它是通过七层协议实现代理转发的,所以只要客户端通过HTTP或HTTPS协议可以连接到正向代理服务器即可。
1.3、内容分发网络
内容分发网络(CDN)是基于反向代理缓存技术实现的大规模网络应用,其将缓存服务器分布到用户访问相对集中的地区或网络中,当用户访问目标网站时,它会利用全局负载策略,将用户的访问分配到离用户最近的缓存服务器,并由被分配的缓存服务器处理用户的访问请求。国内跨运营商的网络访问会很慢,通过CDN的分配策略,可有效地优化网络路径,并结合CDN缓存服务器节点的缓存,有效提高用户的访问速度,从而提升用户体验。内容分发网络架构如图所示。

CDN将被加速的网站内容缓存在离用户最近的缓存服务器中,通常被缓存的是更新较少的静态资源(如静态页面、CSS、JavaScript、图片、视频等),CDN的各缓存服务器节点是通过HTTP响应头的Cache-Control来控制本地内容缓存有效期的。当客户端的请求被分配到CDN缓存服务器节点时,CDN缓存服务器会先判断内容缓存是否过期,若内容缓存在有效期内,则直接返回客户端,否则将向源站点发出回源请求,并从源站点获取最新的数据,在更新本地缓存后将响应数据返回客户端。CDN的缓存有效期设置会影响内容缓存的回源率。如果缓存有效期设置的较长,回源率较低,则会使缓存服务器的缓存数据与源网站不同步,影响访问;如果缓存有效期设置的较短,回源率较高,则会增加源网站的负载,影响CDN缓存服务器的使用效率。因此,CDN服务商会根据被缓存资源的类型(如文件后缀)、路径等多个维度为使用者提供缓存有效期设置接口,并为用户提供更加细化的缓存时间管理。除了可以设置缓存时间外,也可以通过"缓存刷新"接口对CDN缓存服务器的缓存数据进行强制更新。
1.4、反向代理缓存
反向代理缓存是基于反向代理技术在用户请求转发到Web服务器前进行缓存加载的缓存方式。反向代理缓存服务器通常位于Web服务器之前,通过反向代理缓存服务器可以对被代理服务器的响应内容进行缓存,以加速用户请求响应的处理速度,降低被代理服务器的负载。反向代理缓存服务器架构如图所示。

反向代理缓存提高了网站内容的加载速度,降低了被代理服务器的负载,并可以在被代理服务器发生故障时通过缓存的内容作为备份来提高网站的可用性。
- 提升网站性能。反向代理缓存以与静态内容相同的处理速度为所有类型的缓存内容提供用户响应处理,从而减少因被代理服务器动态计算产生的延迟,进一步提升网站的性能。
- 增加资源容量。因为减少了被代理服务器的请求,被代理服务器将有更多的计算资源处理动态响应,相对增加了应用服务器的资源容量。
- 提高可用性。通过反向代理缓存服务器的本地缓存,可以在被代理服务器出现故障或停机产生的故障等待时,让用户仍可访问网站(单向的浏览缓存中的内容),避免了用户因收到故障信息而产生的负面影响。
2、Nginx缓存模块
Nginx的缓存不仅支持HTTP协议代理缓存,还支持如FastCGI、SCGI及uWSGI协议代理的缓存。由于配置指令相似,本章仅以HTTP代理模块的缓存配置指令进行介绍。Nginx缓存有两种配置方式:一种是将静态资源存储在本地,由用户手工维护的镜像方式;另一种是由缓存管理进程自动维护的缓存方式。这两种方式的响应逻辑基本相同,都是将缓存存储在磁盘上,并将缓存内容返回给用户,从而减少了后端被代理服务器的请求操作。
除Nginx自身的缓存处理方案外,Nginx还提供了通过Memcached代理模块的缓存应用方案,Nginx通过Memcached模块与Memcached服务器交互,用Memcached服务器实现缓存存储的方式以提升用户响应速度,同时这个应用方案需要用户自行维护Memcached的内容。
2.1、代理缓存模块
Nginx的缓存功能是集成在代理模块中的,当启用缓存功能时,Nginx将请求返回的响应数据持久化在服务器磁盘中,响应数据缓存的相关元数据、有效期及缓存内容等信息将被存储在定义的共享内存中。当收到客户端请求时,Nginx会在共享内存中搜索缓存信息,并把查询到的缓存数据从磁盘中快速交换到操作系统的页面缓存(PageCache)中,整个过程的速度非常快。Nginx缓存会缓存加载进程(Cache Loader Process)和库存管理(Cade MangerProcess)进行管理。缓存加载进程只在Nginx启动时执行一次,将上一次Nginx运行时缓存有关数据的元数据加载到共享内存区域,加载结束后它将自动退出。为了避免缓存因加载缓存降低Nginx的性能,缓存加载进程会采用周 期性迭代式加载缓存数据,且迭代加载的时间间隔、每次最大消耗时间和每次迭代加载的数量可以由配置指令proxy_cache_path的指令值参数设置。缓存管理进程则周期性的检查缓存的状态,负责清除在一段时间内未被访问的缓存文件,并对超出缓存存储最大值的缓存对象进行删除,缓存管理进程的删除操作也是周期性迭代执行的,并由配置指令proxy_cache_path的指令值参数设置。
2.1.1、缓存处理流程及状态
当客户端发起请求到Nginx缓存服务器时,Nginx会先检查本地是否已经有该请求的内容缓存,有的话会直接返回数据,缓存请求状态会被标记为HIT,否则该缓存请求状态就会被标记为MISS。如果指令proxy_cache_lock未被启用,则会直接向源服务器发起访问请求,如果被启用,则会先确认当前请求是不是第一个发起的请求,若不是,则等待;若是,则向源服务器发起访问请求。服务器响应数据返回后会先被存储在本地缓存,然后再返回给客户端。缓存处理流程如图所示。

Nginx在处理缓存过程中,客户端请求的缓存请求状态会被记录在变量$upstream_cache_status中,缓存请求状态如表所示。


2.1.2、缓存配置指令
Nginx缓存配置指令如表所示。


- 该模块指令列表中指令的指令域范围都是http、server、location。
- proxy_cache_path指令只能编写在http指令域中。
- proxy_cache与proxy_store指令不能在同一指令域中同时使用。
- proxy_cache_path指令值参数如表所示。


2.1.3、HTTP范围请求
范围请求允许服务器只发送请求的一部分响应数据给客户端,通常对大文件传输时,用以实现断点续传、多线程下载等功能。若服务端响应信息头中包含字段Accept-Ranges:bytes,则表示服务端支持范围请求,且节点范围的单位为字节(bytes)。在Nginx缓存默认配置下,Nginx处理完一个大文件的初始请求后,后续的用户请求必须等待整个文件下载结束并存入缓存后才可以继续被处理,整个过程非常耗时。为解决这个问题,Nginx提供了ngx_http_slice_module模块,用以缓存范围请求的支持。该模块将文件分成更小的切片(slices),客户端每个范围请求覆盖特定的切片,如果该范围没有缓存,则从源服务器请求后存入缓存,否则就从缓存中返回数据。http_slice模块配置指令如表所示。

配置样例如下:
bash
location / {
slice 1m; # 切片大小为1MB
proxy_cache cache; # 缓存共享内存名称为cache
proxy_cache_key $uri$is_args$args$slice_range; # 设置缓存key
proxy_set_header Range $slice_range; # 添加头字段Range的字段值为
# $slice_range
proxy_cache_valid 200 206 1h; # 响应状态码为200及206的内容缓存有效期为1h
proxy_pass http://localhost:8000;
}
2.2、Memcached缓存模块
Nginx的ngx_http_memcached_module模块本身并没有提供缓存功能,它只是一个将用户请求转发到Memcached服务器的代理模块。在以Memcached服务器为缓存应用的方案中,Memcached作为内容缓存的存储服务器,用户通过URL为Memcached的key将Web请求数据缓存到Memcached服务器中,在客户端发起请求时,Nginx通过一致的URL为key,快速地从Memcached服务器中将缓存的内容作为用户的请求响应数据返回给客户端。
Memcached是一个开源、高性能的内存对象缓存系统,使用Memcached服务器作为缓存存储服务器,充分利用了Memcached的高效缓存功能,减少了Nginx服务器磁盘I/O的操作,也可以通过upstream指令对多台Memcached做分布式集群负载,以便整体提升Nginx缓存服务器的性能。Memcached缓存模块配置指令如表所示。


配置样例如下:
bash
server {
location / {
set $memcached_key "$uri?$args"; # 设置Memcached缓存key
memcached_pass 127.0.0.1:11211; # 设置被代理Memcached地址
error_page 404 502 504 = @fallback; # 返回状态码404、502、504时跳入内部请求
}
location @fallback {
proxy_pass http://backend; # 将请求转发给后端服务器
}
}