自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊聊高并發(fā)系統(tǒng)之HTTP緩存

開發(fā) 開發(fā)工具
因本文主要以瀏覽器緩存場(chǎng)景介紹,所以非瀏覽器場(chǎng)景下的一些用法本文不會(huì)介紹,而且本文以chrome為測(cè)試瀏覽器。

[[177161]]

簡(jiǎn)介

最近遇到很多人來(lái)咨詢我關(guān)于瀏覽器緩存的一些問(wèn)題,而這些問(wèn)題都是類似的,因此總結(jié)本文來(lái)解答以后遇到類似問(wèn)題的朋友。

因本文主要以瀏覽器緩存場(chǎng)景介紹,所以非瀏覽器場(chǎng)景下的一些用法本文不會(huì)介紹,而且本文以chrome為測(cè)試瀏覽器。

瀏覽器緩存是指當(dāng)我們使用瀏覽器訪問(wèn)一些網(wǎng)站頁(yè)面或者h(yuǎn)ttp服務(wù)時(shí),根據(jù)服務(wù)端返回的緩存設(shè)置響應(yīng)頭將響應(yīng)內(nèi)容緩存到瀏覽器,下次可以直接使用緩存內(nèi)容或者僅需要去服務(wù)端驗(yàn)證內(nèi)容是否過(guò)期即可。這樣的好處可以減少瀏覽器和服務(wù)端之間來(lái)回傳輸?shù)臄?shù)據(jù)量,節(jié)省帶寬提升性能。

首先看個(gè)例子;當(dāng)我們***次訪問(wèn)http://item.jd.com/1856588.html時(shí)將得到如下響應(yīng)頭:

***次訪問(wèn)http://item.jd.com/1856588.html時(shí)將得到如下響應(yīng)頭

 

然后接著按F5刷新頁(yè)面,將得到如下響應(yīng)頭

***次訪問(wèn)http://item.jd.com/1856588.html時(shí)接著按F5刷新當(dāng)前頁(yè)面

第二次返回的相應(yīng)狀態(tài)碼為304,表示服務(wù)端文檔沒有修過(guò)過(guò),瀏覽器緩存的內(nèi)容還是***的。

接下來(lái)我們看下如何在Java應(yīng)用層控制瀏覽器緩存。

示例

Last-Modified

如下是我們的spring mvc緩存測(cè)試代碼:

  1. @RequestMapping("/cache") 
  2. public ResponseEntity<String> cache( 
  3.       HttpServletRequest request, 
  4.       //為了方便測(cè)試,此處傳入文檔***修改時(shí)間 
  5.       @RequestParam("millis") long lastModifiedMillis, 
  6.       //瀏覽器驗(yàn)證文檔內(nèi)容是否修改時(shí)傳入的Last-Modified 
  7.       @RequestHeader (value = "If-Modified-Since"required = false) Date ifModifiedSince) { 
  8.  
  9.     //當(dāng)前系統(tǒng)時(shí)間 
  10.     long now = System.currentTimeMillis(); 
  11.     //文檔可以在瀏覽器端/proxy上緩存多久 
  12.     long maxAge = 20
  13.  
  14.     //判斷內(nèi)容是否修改了,此處使用等值判斷 
  15.     if(ifModifiedSince != null && ifModifiedSince.getTime() == lastModifiedMillis) { 
  16.         return new ResponseEntity<String>(HttpStatus.NOT_MODIFIED); 
  17.     } 
  18.  
  19.     DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 
  20.  
  21.     String body = "<a href=''>點(diǎn)擊訪問(wèn)當(dāng)前鏈接</a>"
  22.     MultiValueMap<String, String> headers = new HttpHeaders(); 
  23.  
  24.     //文檔修改時(shí)間 
  25.     headers.add("Last-Modified", gmtDateFormat.format(new Date(lastModifiedMillis))); 
  26.     //當(dāng)前系統(tǒng)時(shí)間 
  27.     headers.add("Date", gmtDateFormat.format(new Date(now))); 
  28.     //過(guò)期時(shí)間 http 1.0支持 
  29.     headers.add("Expires", gmtDateFormat.format(new Date(now + maxAge))); 
  30.     //文檔生存時(shí)間 http 1.1支持 
  31.     headers.add("Cache-Control", "max-age=" + maxAge); 
  32.     return new ResponseEntity<String>(body, headers, HttpStatus.OK); 

為了方便測(cè)試,測(cè)試時(shí)將文檔的修改時(shí)間通過(guò)millis參數(shù)傳入,實(shí)際應(yīng)用時(shí)可以使用如商品的***修改時(shí)間等替代。

***訪問(wèn)

***訪問(wèn)http://localhost:9080/cache?millis=1471349916709,將得到如下響應(yīng)頭:

***訪問(wèn)http://localhost:9080/cache?millis=1471349916709

響應(yīng)狀態(tài)碼200表示請(qǐng)求內(nèi)容成功,另外有如下幾個(gè)緩存控制參數(shù):

  • Last-Modified:表示文檔的***修改時(shí)間,當(dāng)去服務(wù)器驗(yàn)證時(shí)會(huì)拿這個(gè)時(shí)間去;
  • Expires:http/1.0規(guī)范定義,表示文檔在瀏覽器中的過(guò)期時(shí)間,當(dāng)緩存的內(nèi)容超過(guò)這個(gè)時(shí)間則需要重新去服務(wù)器獲取***的內(nèi)容;
  • Cache-Control:http/1.1規(guī)范定義,表示瀏覽器緩存控制,max-age=20表示文檔可以在瀏覽器中緩存20秒。

根據(jù)規(guī)范定義Cache-Control優(yōu)先級(jí)高于Expires;實(shí)際使用時(shí)可以兩個(gè)都用,或僅使用Cache-Control就可以了(比如京東的活動(dòng)頁(yè)sale.jd.com)。一般情況下Expires=當(dāng)前系統(tǒng)時(shí)間(Date) + 緩存時(shí)間(Cache-Control: max-age)。大家可以在如上測(cè)試代碼進(jìn)行兩者單獨(dú)測(cè)試,緩存都是可行的。

F5刷新

接著按F5刷新當(dāng)前頁(yè)面,將看到瀏覽器發(fā)送如下請(qǐng)求頭:

***訪問(wèn)http://localhost:9080/cache?millis=1471349916709,接著按F5刷新當(dāng)前頁(yè)面

此處發(fā)送時(shí)有一個(gè)If-Modified-Since請(qǐng)求頭,其值是上次請(qǐng)求響應(yīng)中的Last-Modified,即瀏覽器會(huì)拿這個(gè)時(shí)間去服務(wù)端驗(yàn)證內(nèi)容是否發(fā)生了變更。接著收到如下響應(yīng)信息:

If-Modified-Since請(qǐng)求頭

響應(yīng)狀態(tài)碼為304,表示服務(wù)端告訴瀏覽器說(shuō)“瀏覽器你緩存的內(nèi)容沒有變化,直接使用緩存內(nèi)容展示吧”。

注:在測(cè)試時(shí)要過(guò)一段時(shí)間更改下參數(shù)millis來(lái)表示內(nèi)容修改了,要不然會(huì)一直看到304響應(yīng)。

Ctrl+F5強(qiáng)制刷新

如果你想,可以按Ctrl+F5:

強(qiáng)制從服務(wù)端獲取***的內(nèi)容

瀏覽器在請(qǐng)求時(shí)不會(huì)帶上If-Modified-Since,并帶上Cache-Control:no-cache和Pragma:no-cache,這是為了告訴服務(wù)端說(shuō)我請(qǐng)給我一份***的內(nèi)容。

from cache

當(dāng)我們按F5刷新、Ctrl+F5強(qiáng)制刷新、地址欄輸入地址刷新時(shí)都會(huì)去服務(wù)端驗(yàn)證內(nèi)容是否發(fā)生了變更。那什么情況才不去服務(wù)端驗(yàn)證呢?即有些朋友還會(huì)發(fā)現(xiàn)有一些“from cache”的情況,這是什么情況下發(fā)生的呢?

從A頁(yè)面跳轉(zhuǎn)到A頁(yè)面或者從A頁(yè)面跳轉(zhuǎn)到B頁(yè)面時(shí):

from cache

大家可以通過(guò)如上方式模擬,即從A頁(yè)面跳轉(zhuǎn)到A頁(yè)面也是情況1。此時(shí)如果內(nèi)容還在緩存時(shí)間之內(nèi),直接從瀏覽器獲取的內(nèi)容,而不去服務(wù)端驗(yàn)證。

訪問(wèn)頁(yè)面http://item.jd.com/11056556.html,然后點(diǎn)擊面包屑中的HTTP權(quán)威指南時(shí)會(huì)跳轉(zhuǎn)到當(dāng)前頁(yè)面,此時(shí)看到如下結(jié)果,頁(yè)面及頁(yè)面異步加載的一些js、css、圖片都from cache了。

頁(yè)面及頁(yè)面異步加載的一些js、css、圖片都from cache

還有如通過(guò)瀏覽器歷史記錄進(jìn)行前進(jìn)后退時(shí)也會(huì)走from cache。本文是基于chrome 52.0.2743.116 m版本測(cè)試,不同瀏覽器行為可能存在差異。

Age

一般用于代理層(如CDN),大家在訪問(wèn)京東一些頁(yè)面時(shí)會(huì)發(fā)現(xiàn)有一個(gè)Age響應(yīng)頭,然后強(qiáng)制刷新(Ctrl+F5)后會(huì)發(fā)現(xiàn)其不斷的變化;其表示此內(nèi)容在代理層從緩存到現(xiàn)在經(jīng)過(guò)了多長(zhǎng)時(shí)間了,即在代理層緩存了多長(zhǎng)時(shí)間了。

Age響應(yīng)頭

Vary

一般用于代理層(如CDN),用于代理層和瀏覽器協(xié)商什么情況下使用哪個(gè)版本的緩存內(nèi)容(比如壓縮版和非壓縮版),即什么情況下后續(xù)請(qǐng)求才能使用代理層緩存的該版本內(nèi)容,比如如下響應(yīng)是告知瀏覽器Content-Encoding:gzip,即緩存代理層緩存了gzip版本的內(nèi)容;那么后續(xù)的請(qǐng)求在請(qǐng)求時(shí)Accept-Encoding頭部中包含gzip時(shí)才能使用改代理層緩存。

告知瀏覽器Content-Encoding:gzip

Via

一般用于代理層(如CDN),表示訪問(wèn)到最終內(nèi)容經(jīng)過(guò)了哪些代理層,用的什么協(xié)議,代理層是否緩存***等等;通過(guò)它可以進(jìn)行一些故障診斷。

通過(guò)Via可以進(jìn)行一些故障診斷

ETag

  1. @RequestMapping("/cache/etag") 
  2. public ResponseEntity<String> cache( 
  3.       HttpServletRequest request, 
  4.       HttpServletResponse response, 
  5.       //瀏覽器驗(yàn)證文檔內(nèi)容的實(shí)體 If-None-Match 
  6.       @RequestHeader (value = "If-None-Match"required = false) String ifNoneMatch) { 
  7.  
  8.     //當(dāng)前系統(tǒng)時(shí)間 
  9.     long now = System.currentTimeMillis(); 
  10.     //文檔可以在瀏覽器端/proxy上緩存多久 
  11.     long maxAge = 10
  12.  
  13.     String body = "<a href=''>點(diǎn)擊訪問(wèn)當(dāng)前鏈接</a>"
  14.  
  15.     //弱實(shí)體 
  16.     String etag = "W/\"" + md5(body) + "\""; 
  17.  
  18.     if(StringUtils.equals(ifNoneMatch, etag)) { 
  19.         return new ResponseEntity<String>(HttpStatus.NOT_MODIFIED); 
  20.     } 
  21.  
  22.     DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 
  23.     MultiValueMap<String, String> headers = new HttpHeaders(); 
  24.  
  25.     //ETag http 1.1支持 
  26.     headers.add("ETag", etag);  
  27.     //當(dāng)前系統(tǒng)時(shí)間 
  28.     headers.add("Date", gmtDateFormat.format(new Date(now))); 
  29.     //文檔生存時(shí)間 http 1.1支持 
  30.     headers.add("Cache-Control", "max-age=" + maxAge); 
  31.     return new ResponseEntity<String>(body, headers, HttpStatus.OK); 

其中ETag用于發(fā)送到服務(wù)端進(jìn)行內(nèi)容變更驗(yàn)證的,而Catch-Control是用于控制緩存時(shí)間的(瀏覽器、代理層等)。此處我們使用了弱實(shí)體W\”343sda”,弱實(shí)體(”343sda”)只要內(nèi)容語(yǔ)義沒變即可,比如內(nèi)容的gzip版和非gzip版可以使用弱實(shí)體驗(yàn)證;而強(qiáng)實(shí)體指字節(jié)必須完全一致(gzip和非gzip情況是不一樣的),因此建議首先選擇使用弱實(shí)體。nginx在生成etag時(shí)使用的算法是Last-Modified + Content-Length計(jì)算的:

ngx_sprintf(etag->value.data,"\"%xT-%xO\"",

r->headers_out.last_modified_time,

r->headers_out.content_length_n)

到此簡(jiǎn)單的基于文檔修改時(shí)間和過(guò)期時(shí)間的緩存控制就介紹完了,在內(nèi)容型響應(yīng)我們大多數(shù)根據(jù)內(nèi)容的修改時(shí)間來(lái)進(jìn)行緩存控制,ETag根據(jù)實(shí)際需求而定(比如)。另外還可以使用html Meta標(biāo)簽控制瀏覽器緩存,但是對(duì)代理層緩存無(wú)效,因此不建議使用。

總結(jié)

Last-Modified與ETag同時(shí)使用

1、服務(wù)端響應(yīng)的Last-Modified會(huì)在下次請(qǐng)求時(shí)以If-Modified-Since請(qǐng)求頭帶到服務(wù)端進(jìn)行文檔是否修改的驗(yàn)證,如果沒有修改則返回304,瀏覽器可以直接使用緩存內(nèi)容;

2、Cache-Control:max-age和Expires用于決定瀏覽器端內(nèi)容緩存多久,即多久過(guò)期,過(guò)期后則刪除緩存重新從服務(wù)端獲取***的;另外可以用于from cache場(chǎng)景;

3、http/1.1規(guī)范定義的Cache-Control優(yōu)先級(jí)高于http/1.0規(guī)范定義的Expires;

4、一般情況下Expires=當(dāng)前系統(tǒng)時(shí)間 + 緩存時(shí)間(Cache-Control:max-age);

5、http/1.1規(guī)范定義了ETag來(lái)通過(guò)文檔摘要的方式控制。

Last-Modified與ETag同時(shí)使用時(shí),瀏覽器在驗(yàn)證時(shí)會(huì)同時(shí)發(fā)送If-Modified-Since和If-None-Match,按照http/1.1規(guī)范,如果同時(shí)使用If-Modified-Since和If-None-Match則服務(wù)端必須兩個(gè)都驗(yàn)證通過(guò)后才能返回304;且nginx就是這樣做的。因此實(shí)際使用時(shí)應(yīng)該根據(jù)實(shí)際情況選擇。還有If-Match和If-Unmodified-Since本文就不介紹了。

接下來(lái)我們看下如何使用nginx進(jìn)行緩存控制。

nginx緩存設(shè)置

nginx提供了expires、etag、if-modified-since指令來(lái)進(jìn)行瀏覽器緩存控制。

expires

假設(shè)我們使用nginx作為靜態(tài)資源服務(wù)器,此時(shí)可以使用expires進(jìn)行緩存控制。

  1. location /img { 
  2.   alias /export/img/; 
  3.   expires 1d; 

當(dāng)我們?cè)L問(wèn)靜態(tài)資源時(shí),如http://192.168.61.129/img/1.jpg,將得到類似如下的響應(yīng)頭:

訪問(wèn)靜態(tài)資源時(shí),如http://192.168.61.129/img/1.jpg

對(duì)于靜態(tài)資源會(huì)自動(dòng)添加ETag,可以通過(guò)添加“etag off”指令禁止生成ETag。如果是靜態(tài)文件Last-Modified是文件的***修改時(shí)間;Expires是根據(jù)當(dāng)前服務(wù)端系統(tǒng)時(shí)間算出來(lái)的。如上nginx配置的計(jì)算邏輯(實(shí)際計(jì)算邏輯比這個(gè)多,具體參考官方文檔):

  1. if (expires == NGX_HTTP_EXPIRES_ACCESS ||r->headers_out.last_modified_time == -1) { 
  2.    max_age = expires_time
  3.    expires_time += now; 
  4.   

此指令用于表示nginx如何拿服務(wù)端的Last-Modified和瀏覽器端的If-Modified-Since時(shí)間進(jìn)行比較,默認(rèn)“if_modified_since exact”表示精確匹配,也可以使用“if_modified_sincebefore”表示只要文件的上次修改時(shí)間早于或等于瀏覽器短的If-Modified-Since時(shí)間,就返回304。

nginx proxy expires

使用nginx作為反向代理時(shí),請(qǐng)求會(huì)先進(jìn)入nginx,然后nginx將請(qǐng)求轉(zhuǎn)發(fā)給后端應(yīng)用。如下圖所示:

首先配置upstream:

  1. upstream backend_tomcat { 
  2.    server 192.168.61.1:9080 max_fails=10 fail_timeout=10s weight=5

接著配置location:

  1. upstream backend_tomcat { 
  2.    server 192.168.61.1:9080 max_fails=10 fail_timeout=10s weight=5

接下來(lái)我們可以通過(guò)如http://192.168.61.129/cache?millis=1471349916709訪問(wèn)nginx,nginx會(huì)將請(qǐng)求轉(zhuǎn)發(fā)給后端java應(yīng)用。也就是說(shuō)nginx只是做了相關(guān)的轉(zhuǎn)發(fā)(負(fù)載均衡),并沒有對(duì)請(qǐng)求和響應(yīng)做什么處理。

假設(shè)對(duì)后端返回的過(guò)期時(shí)間需要調(diào)整,可以添加expires指令到location:

  1. location = /cache { 
  2.     proxy_pass http://backend_tomcat/cache$is_args$args; 
  3.     expires 5s; 

然后再請(qǐng)求相關(guān)的URL,將得到如下響應(yīng):

nginx proxy expires

過(guò)期時(shí)間相關(guān)的響應(yīng)頭被expires指令更改了,但是Last-Modified是沒有變的。

即使我們更改了緩存過(guò)期頭,但nginx本身沒有對(duì)這些內(nèi)容做緩存,每次請(qǐng)求還是要到后端驗(yàn)證的,假設(shè)在過(guò)期時(shí)間內(nèi),這些驗(yàn)證在nginx這一層驗(yàn)證就可以了,不需要到后端驗(yàn)證,這樣可以減少后端的很大壓力。即整體流程是:

1、瀏覽器發(fā)起請(qǐng)求,首先到nginx,nginx根據(jù)url在nginx本地查找是否有文檔緩存;

2、nginx沒有找到本地緩存,則去后端獲取***的文檔,并放入到nginx本地緩存中;返回200狀態(tài)碼和***的文檔給瀏覽器;

3、nginx找到本地緩存了,首先驗(yàn)證文檔是否過(guò)期(Cache-Control:max-age=5),如果過(guò)期則去后端獲取***的文檔,并放入nginx本地緩存中,返回200狀態(tài)碼和***的文檔給瀏覽器;如果文檔沒有過(guò)期,如果If-Modified-Since與緩存文檔的Last-Modified匹配,則返回300狀態(tài)碼給瀏覽器,否則返回200狀態(tài)碼和***的文檔給瀏覽器。

即內(nèi)容不需要?jiǎng)討B(tài)(計(jì)算、渲染等)速度更快,內(nèi)容越接近于用戶速度越快。像apache traffic server、squid、varnish、nginx等技術(shù)都可以來(lái)進(jìn)行內(nèi)容緩存。還有CDN就是用來(lái)加速用戶訪問(wèn)的:

CDN就是用來(lái)加速用戶訪問(wèn)的

即用戶首先訪問(wèn)到全國(guó)各地的CDN節(jié)點(diǎn)(使用如ATS、Squid實(shí)現(xiàn)),如果CDN沒***,會(huì)回源到中央nginx集群,該集群如果沒有***緩存(該集群的緩存不是必須的,要根據(jù)實(shí)際***情況等決定),***回源到后端應(yīng)用集群。

像我們商品詳情頁(yè)的一些服務(wù)就大量使用了nginx緩存減少回源到后端的請(qǐng)求量,從而提升訪問(wèn)速度??梢詤⒖肌稑?gòu)建需求響應(yīng)式億級(jí)商品詳情頁(yè)》、《京東商品詳情頁(yè)服務(wù)閉環(huán)實(shí)踐》和《應(yīng)用多級(jí)緩存模式支撐海量讀服務(wù)》。

nginx代理層緩存

http模塊配置:

proxy_buffering on;

proxy_buffer_size 4k;

proxy_buffers 512 4k;

proxy_busy_buffers_size 64k;

proxy_cache_path /export/cache/proxy_cachelevels=1:2 keys_zone=cache:512m inactive=5m max_size=8g use_temp_path=off;

#proxy timeout

proxy_connect_timeout 3s;

proxy_read_timeout 5s;

proxy_send_timeout 5s;

其中紅色部分是proxy_cache_path指令相關(guān)配置:

  • levels=1:2 :表示創(chuàng)建兩級(jí)目錄結(jié)構(gòu),比如/export/cache/proxy_cache/7/3c/,將所有文件放在一級(jí)目錄結(jié)構(gòu)中如果文件量很大會(huì)導(dǎo)致訪問(wèn)文件慢;keys_zone=cache:512m :設(shè)置存儲(chǔ)所有緩存key和相關(guān)信息的共享內(nèi)存區(qū),1M大約能存儲(chǔ)8000個(gè)key;
  • inactive=5m :inactive指定被緩存的內(nèi)容多久不被訪問(wèn)將從緩存中移除,以保證內(nèi)容的新鮮;默認(rèn)10分鐘;
  • max_size=8g :***緩存閥值,“cachemanager”進(jìn)程會(huì)監(jiān)控***緩存大小,當(dāng)緩存達(dá)到該閥值,該進(jìn)程將從緩存中移除最近最少使用的內(nèi)容;
  • use_temp_path:如果為on,則內(nèi)容首先被寫入臨時(shí)文件(proxy_temp_path ),然后重命名到proxy_cache_path指定的目錄;如果設(shè)置為off,則內(nèi)容直接被寫入到proxy_cache_path指定的目錄,如果需要cache建議off,該特性是1.7.10提供的。

location配置

  1. location = /cache { 
  2.      proxy_cache cache; 
  3.      proxy_cache_key $scheme$proxy_host$request_uri; 
  4.      proxy_cache_valid 200 5s; 
  5.      proxy_pass http://backend_tomcat/cache$is_args$args; 
  6.     add_header cache-status $upstream_cache_status; 

緩存相關(guān)配置:

  • proxy_cache :指定使用哪個(gè)共享內(nèi)存區(qū)域存儲(chǔ)緩存鍵和相關(guān)信息;
  • proxy_cache_key :設(shè)置緩存使用的key,默認(rèn)為訪問(wèn)的完整URL,根據(jù)實(shí)際情況設(shè)置緩存key;
  • proxy_cache_valid :為不同的響應(yīng)狀態(tài)碼設(shè)置緩存時(shí)間;如果是proxy_cache_valid 5s 則200、301、302響應(yīng)將被緩存;

proxy_cache_valid

proxy_cache_valid不是唯一設(shè)置緩存時(shí)間的,還可以通過(guò)如下方式(優(yōu)先級(jí)從上到下):

1、以秒為單位的“X-Accel-Expires”響應(yīng)頭來(lái)設(shè)置響應(yīng)緩存時(shí)間;

2、如果沒有“X-Accel-Expires”,可以根據(jù)“Cache-Control”、“Expires”來(lái)設(shè)置響應(yīng)緩存時(shí)間;

3、否則使用proxy_cache_valid設(shè)置的緩存時(shí)間;

如果響應(yīng)頭包含Cache-Control:private/no-cache/no-store、Set-Cookie或者只有一個(gè)Vary響應(yīng)頭且其值為*,則響應(yīng)內(nèi)容將不會(huì)被緩存??梢允褂胮roxy_ignore_headers來(lái)忽略這些響應(yīng)頭。

add_headercache-status $upstream_cache_status在響應(yīng)頭中添加緩存***的狀態(tài):

  • HIT:緩存***了,直接返回緩存中內(nèi)容,不回源到后端;
  • MISS:緩存沒有***,回源到后端獲取***的內(nèi)容;
  • EXPIRED:緩存***但過(guò)期了,回源到后端獲取***的內(nèi)容;
  • UPDATING:緩存已過(guò)期但正在被別的nginx進(jìn)程更新;配置了proxy_cache_use_staleupdating指令時(shí)會(huì)存在該狀態(tài);
  • STALE:緩存已過(guò)期,但因后端服務(wù)出現(xiàn)了問(wèn)題(比如后端服務(wù)掛了)返回過(guò)期的響應(yīng);配置了如proxy_cache_use_stale error timeout指令后會(huì)存在該狀態(tài);
  • REVALIDATED:?jiǎn)⒂胮roxy_cache_revalidate指令后,當(dāng)緩存內(nèi)容過(guò)期時(shí)nginx通過(guò)一次If-Modified-Since的請(qǐng)求頭去驗(yàn)證緩存內(nèi)容是否過(guò)期,此時(shí)會(huì)返回該狀態(tài);
  • BYPASS:proxy_cache_bypass指令有效時(shí)強(qiáng)制回源到后端獲取內(nèi)容,即使已經(jīng)緩存了;

proxy_cache_min_uses

用于控制請(qǐng)求多少次后響應(yīng)才被緩存;默認(rèn)“proxy_cache_min_uses 1;”,如果緩存熱點(diǎn)比較集中、存儲(chǔ)有限,可以考慮修改該參數(shù)以減少緩存數(shù)量和寫磁盤次數(shù);

proxy_no_cache

用于控制什么情況下響應(yīng)將不被緩存;比如配置“proxy_no_cache $args_nocache”,如果帶的參數(shù)值至少有一個(gè)不為空或者0,則響應(yīng)將不被緩存;

proxy_cache_bypass

類似于proxy_no_cache,但是其控制什么情況不從緩存中獲取內(nèi)容,而是直接到后端獲取內(nèi)容;如果***則$upstream_cache_status為BYPASS;

proxy_cache_use_stale

當(dāng)對(duì)緩存內(nèi)容的過(guò)期時(shí)間不敏感,或者后端服務(wù)出問(wèn)題時(shí)即使緩存的內(nèi)容不新鮮也總比返回錯(cuò)誤給用戶強(qiáng)(類似于托底),此時(shí)可以配置該參數(shù),如“proxy_cache_use_stale error timeout http_500 http_502 http_503http_504”:即如果超時(shí)、后端連接出錯(cuò)、500、502、503等錯(cuò)誤時(shí)即使緩存內(nèi)容已過(guò)期也先返回給用戶,此時(shí)$upstream_cache_status為STALE;還有一個(gè)updating表示緩存已過(guò)期但正在被別的nginx進(jìn)程更新將先返回過(guò)期的內(nèi)容,此時(shí) $upstream_cache_status為UPDATING;

proxy_cache_revalidate

當(dāng)緩存過(guò)期后,如果開啟了proxy_cache_revalidate,則會(huì)發(fā)出一次If-Modified-Since和If-None-Match條件請(qǐng)求,如果后端返回304則會(huì)得到兩個(gè)好處:節(jié)省帶寬和減少寫磁盤的次數(shù);此時(shí)$upstream_cache_status為REVALIDATED;

proxy_cache_lock

當(dāng)多個(gè)客戶端同時(shí)請(qǐng)求同一份內(nèi)容時(shí),如果開啟proxy_cache_lock(默認(rèn)off)則只有一個(gè)請(qǐng)求被發(fā)送至后端;其他請(qǐng)求將等待該內(nèi)容返回;當(dāng)***個(gè)請(qǐng)求返回時(shí),其他請(qǐng)求將從緩存中獲取內(nèi)容返回;當(dāng)***個(gè)請(qǐng)求超過(guò)了proxy_cache_lock_timeout超時(shí)時(shí)間(默認(rèn)5s),則其他請(qǐng)求將同時(shí)請(qǐng)求到后端來(lái)獲取響應(yīng),且響應(yīng)不會(huì)被緩存(在1.7.8版本之前是被緩存的);啟用proxy_cache_lock可以應(yīng)對(duì)Dog-pile effect(當(dāng)某個(gè)緩存失效時(shí),同時(shí)又大量相同的請(qǐng)求沒***緩存,而同時(shí)請(qǐng)求到后端,從而導(dǎo)致后端壓力太大,此時(shí)限制一個(gè)請(qǐng)求去拿即可)。

proxy_cache_lock_age是1.7.8新添加的,如果在proxy_cache_lock_age指定的時(shí)間內(nèi)(默認(rèn)5s),***一個(gè)發(fā)送到后端進(jìn)行新緩存構(gòu)建的請(qǐng)求還沒有完成,則下一個(gè)請(qǐng)求將被發(fā)送到后端來(lái)構(gòu)建緩存(因?yàn)?.7.8版本之后,proxy_cache_lock_timeout超時(shí)之后返回的內(nèi)容是不緩存的,需要下一次請(qǐng)求來(lái)構(gòu)建響應(yīng)緩存)。

清理緩存

有時(shí)候緩存的內(nèi)容是錯(cuò)誤的,需要手工清理,nginx plus版本提供了purger的功能,但是對(duì)于非plus版本的nginx可以考慮使用ngx_cache_purge(https://github.com/FRiCKLE/ngx_cache_purge)模塊進(jìn)行清理緩存,如:

  1. location ~ /purge(/.*) { 
  2.    allow              127.0.0.1; 
  3.    deny               all; 
  4.    proxy_cache_purge  cache$1$is_args$args; 

注意該方法應(yīng)該只允許內(nèi)網(wǎng)可以訪問(wèn),如有必要可以考慮需要密碼才能訪問(wèn)。

到此代理層緩存就介紹完了,通過(guò)代理層緩存可以解決很多問(wèn)題,可以參考《京東商品詳情頁(yè)服務(wù)閉環(huán)實(shí)踐》和《京東商品詳情頁(yè)服務(wù)閉環(huán)實(shí)踐》。

一些經(jīng)驗(yàn)

1、只緩存200狀態(tài)碼的響應(yīng),像302等要根據(jù)實(shí)際場(chǎng)景決定(比如當(dāng)系統(tǒng)出錯(cuò)時(shí)自動(dòng)302到錯(cuò)誤頁(yè)面,此時(shí)緩存302就不對(duì)了);

2、有些頁(yè)面不需要強(qiáng)一致,可以進(jìn)行幾秒的緩存(比如商品詳情頁(yè)展示的庫(kù)存,可以緩存幾秒鐘,短時(shí)間的不一致對(duì)于用戶來(lái)說(shuō)是沒有影響的);

3、js/css/image等一些內(nèi)容緩存時(shí)間可以設(shè)置的很久(比如1個(gè)月甚至1年),通過(guò)在頁(yè)面修改版本來(lái)控制過(guò)期,不建議隨機(jī)數(shù)方式;

4、假設(shè)商品詳情頁(yè)異步加載的一些數(shù)據(jù)使用的是Last-Modified進(jìn)行的過(guò)期控制,而服務(wù)端做了邏輯修改但內(nèi)容是沒有修改的,即內(nèi)容的***修改時(shí)間沒變,如果想過(guò)期這些異步加載的數(shù)據(jù),可以考慮在商品詳情頁(yè)添加異步加載數(shù)據(jù)的版本號(hào),通過(guò)添加版本號(hào)來(lái)加載***的數(shù)據(jù),或者將Last-Modified時(shí)間加1來(lái)解決;而這種情況比較適合使用ETag;

5、商品詳情頁(yè)異步加載的一些數(shù)據(jù),可以考慮更長(zhǎng)時(shí)間的緩存(比如1個(gè)月而不是幾分鐘),可以通過(guò)MQ將修改時(shí)間推送商品詳情頁(yè),從而實(shí)現(xiàn)按需過(guò)期數(shù)據(jù);

6、服務(wù)端考慮使用內(nèi)存緩存(tmpfs)、SSD緩存;考慮服務(wù)端負(fù)載均衡算法,如一致性哈希提升緩存***率;

7、緩存KEY要合理設(shè)計(jì)(比如去掉參數(shù)/排序參數(shù)保證代理層緩存***),要有清理緩存的工具,出問(wèn)題時(shí)能快速清理掉問(wèn)題KEY;

8、AB測(cè)試/個(gè)性化需求時(shí)應(yīng)禁用掉瀏覽器緩存,但考慮服務(wù)端緩存;

9、為了便于查找問(wèn)題,一般會(huì)在響應(yīng)頭中添加源服務(wù)器信息,如訪問(wèn)京東商品詳情頁(yè)會(huì)看到ser響應(yīng)頭,此頭存儲(chǔ)了源服務(wù)器IP,以便出現(xiàn)問(wèn)題時(shí)知道哪臺(tái)服務(wù)器有問(wèn)題。

【本文是51CTO專欄作者張開濤的原創(chuàng)文章,作者微信公眾號(hào):開濤的博客( kaitao-1234567)】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來(lái)源: 開濤的博客
相關(guān)推薦

2016-11-28 08:40:17

系統(tǒng)降級(jí)服務(wù)

2016-11-25 00:45:37

隊(duì)列數(shù)據(jù)

2016-11-28 08:58:43

系統(tǒng)限流算法

2016-11-28 08:58:43

系統(tǒng)限流

2016-11-28 09:08:43

java系統(tǒng)異步非阻塞

2020-08-27 08:17:05

緩存高并發(fā)系統(tǒng)

2017-12-12 14:51:15

分布式緩存設(shè)計(jì)

2016-11-25 00:38:45

隔離負(fù)載均衡系統(tǒng)

2022-06-12 06:45:26

高并發(fā)防重

2021-10-28 09:36:12

高并發(fā)數(shù)據(jù)實(shí)踐

2022-04-27 09:28:11

HTTPExpires

2018-10-23 10:47:03

高并發(fā)系統(tǒng)緩存

2017-02-20 07:47:04

緩存HASH高并發(fā)

2019-12-03 10:46:07

PHP高并發(fā)架構(gòu)

2018-09-15 04:59:01

2023-07-03 09:59:00

并發(fā)編程并發(fā)容器

2024-02-29 18:06:39

HTTP性能優(yōu)化

2021-02-26 13:08:27

Java高并發(fā)AQS

2021-03-28 09:45:05

冪等性接口數(shù)據(jù)

2021-07-03 17:44:34

并發(fā)高并發(fā)原子性
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)