Nginx 减轻缓存失效对上游服务压力 proxy_cahce_lock和proxy_cache_use_stale

合并回源请求场景


Nginx的缓存满足了绝大部分的应用场景,当我们的服务面对非常大的流量的时候,如果我们的缓存服务例如nginx出现了一些问题,比如新增加了或者一些nginx宕机了,缓存已经失效了,这些ngixn起来的时候会导致大量的请求穿透nginx,因为当前nginx缓存都是失效的,这样全部的请求打到了上游服务,特别是对一些热点的文件,很多用户访问的是同一个资源,但是因为这个资源突然失效了,这些并发的请求全部打到了上游服务,而导致上游服务一直起不来。Nginx对于这种场景有很多的处理方法。

Nginx是怎么样通过合并回源请求等等方式来减轻上游服务在高峰期并发访问压力的呢?

 

合并回源请求,减轻峰值流量下的压力 proxy_cache_lock


最近使用nginx做缓存,发现当大量客户端访问一个没有cache的文件时,回源的流量非常大,在站源上查看日志也看到确实有并发的请求。这个就是需要改成合并回源,当cache内没有数据的时候,只允许1个请求去站源请求数据,写到本地cache。nginx从1.1.12开始原生支持合并回源了。主要有2个配置项(proxy_cache_lock和proxy_cache_lock_timeout控制,开源nginx通过配置proxy_cache_lock指令,可以在一段时间内合并回源请求。但后续请求必须等第一个请求从上游获取完整响应并注入cache后,才会继续往下处理。). 

proxy_cache_lock被启用时,当多个客户端请求一个缓存中不存在的文件(或称之为一个MISS),只有这些请求中的第一个被允许发送至服务器。其他请求在第一个请求得到满意结果之后在缓存中得到文件。如果不启用proxy_cache_lock,则所有在缓存中找不到文件的请求都会直接与服务器通信。

指令 说明
proxy_cache_lock 启用这个指令,当多个客户端请求一个缓存中不存在的文件(或称之为一个MISS),只有这些请求中的第一个被允许发送至服务器。其他请求在第一个请求得到满意结果之后在缓存中得到文件。
proxy_cache_lock_timeout 设置proxy_cache_lock的超时时间。当time到期时,请求将被传递给代理的服务器,但是,响应不会被缓存。
Syntax: proxy_cache_lock on | off;
Default: proxy_cache_lock off;
Context: http, server, location

Syntax: proxy_cache_lock_timeout time;
Default: proxy_cache_lock_timeout 5s;
Context: http, server, location

Syntax: proxy_cache_lock_age time;
Default: proxy_cache_lock_age 5s;
Context: http, server, location
是一个请求发挥响应超时时间,到达后再放行一个请求发往上游

合并回源请求:如果是正常的情况下,nginx对某个热点资源没有做缓存还有四个客户端同时访问的话,那么这四个请求全部都打到了上游服务,那么怎么样解决这个问题呢?

可以使用proxy_cahce_lock指令,这个指令默认是关闭的,当我们打开为on的时候,同一时间,仅第一个请求可以发到上游,其他请求要求等待第一个响应返回或者超时以后来使用缓存响应客户端。

如图:第一步,第一个客户端发来的请求到达nginx,第二步,第二个客户端也发来了请求,nginx让第二个客户端请求其在此等待,也不会向第二个客户端发送响应,也不会发送到上游服务。第三步,nginx将第一个请求向上游发送了,而这个时候第三个客户端发来请求nginx告诉其要求再次等待,第四个客户端发来请求也在此等待,第六步上游服务器终于发回来了响应,第七步,先向第一个客户端发送响应,同时由于已经有缓存了,所以会向第二个客户端,第三个客户端,第四个客户端根据缓存发送响应,这就大大的减轻了上游服务的压力。

 

为了控制这个流程还有两个指令,第一个指令是proxy_cache_lock_timeout,默认是5秒,表示等待第一个请求返回响应的最大时间,也就是说针对的是2,3,4客户端,当2,3,4客户端等待了5秒以后,第一个客户端请求还没有生成响应的缓存,那么2,3,4请求直接同时打向上游服务器。

第二个指令是proxy_cahce_lock_age,第一个请求返回响应的超时时间达到了达到了默认的5秒了,再放行第二个请求,第三个第四个依次等待,依次放行。

 

减少回源请求,使用stale陈旧缓存


陈旧总比没有强

这种方式是虽然我的缓存失效了,但是使用旧的缓存给客户,这样给用户的体验是比较好的。在这种场景下面宁愿使用旧的缓存。

NGINX内容缓存的一个非常强大的特性是:当无法从原始服务器获取最新的内容时,NGINX可以分发缓存中的陈旧(stale,编者注:即过期内容)内容。这种情况一般发生在关联缓存内容的原始服务器宕机或者繁忙时。比起对客户端传达错误信息,NGINX可发送在其内存中的陈旧的文件。NGINX的这种代理方式,为服务器提供额外级别的容错能力,并确保了在服务器故障或流量峰值的情况下的正常运行。为了开启该功能,只需要添加proxy_cache_use_stale命令即可:

location / {
    ...
    proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
}
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 ...;
Default: proxy_cache_use_stale off;
Context: http, server, location


Syntax: proxy_cache_background_update on | off;
Default: proxy_cache_background_update off;
Context: http, server, location
#当使用proxy_cache_use_stale允许使用过期响应时,将同步产生一个子请求,通过访问上游服务更新缓存

Syntax: proxy_cache_revalidate on | off;
Default: proxy_cache_revalidate off;
Context: http, server, location
#更新缓存时候,使用If-Modifiled-Since和If-None-Match作为请求头部,预期内容未发生变更时候通过304减少传输内容

当proxy_cache_use_stale引入updating的时候用原理下图表示:

第一步,第一个客户端发来请求,紧接着第二个客户端发来请求,实际上客户端发送的第一个请求之后nginx就准备将该请求向上游发送了,在第三步向上游发送请求要求重新更新缓存,这个时候第四步第三个客户端发来了请求,因为有旧缓存,因为配置了updating,所以在第五步和第六步分别向第二个或者第三个客户端发送我的旧缓存内容。

第七步上游服务终于发回来响应更新缓存了,此时第四个客户端发来的请求就可以使用新缓存了,最后第九步第十步都将新缓存的内容返回给客户端。

proxy_cache_revalidate指令:

该指可以减轻上游服务的负担,更新缓存时候使用if-mofified-since和if-none-match作为请求头部,预期内容未发生变更时通过304来减少传输内容(if-none-match后面跟着etag,if-modified-since后面跟着last-modified)

我希望上游服务和nginx之间发送响应的时候,不要总是发送200,如果缓存没有过期返回304就可以了,不需要返回完整的文件内容,这个和浏览器与nginx交互时候的流程是一模一样的。

 

如上图所示:proxy_cache_revalidate设置为on,访问的时候nginx与上游可能会发生200,设置为on了之后,会将etag,if-modified-since传入上游服务,上游服务返回304而不用返回200很大的body了。

上面是nginx在峰值情况下去减轻上游服务的压力,在生产环境面对大流量的服务,上面这些指令可以给我们提供很大的帮助,他可以在突发故障的时候保证我们的nginx不会对上游服务产生巨大的流量冲击。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章