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不會對上游服務產生巨大的流量衝擊。

 

 

 

 

 

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