聲明:本位來自京東張開濤的微信公眾號(kaitao-1234567),授權(quán)CSDN轉(zhuǎn)載,,如需轉(zhuǎn)載請聯(lián)系作者,。
作者:張開濤,京東資深Java工程師,,2014年加入京東,,主要負責商品詳情頁、詳情頁統(tǒng)一服務(wù)架構(gòu)與開發(fā)工作,,設(shè)計并開發(fā)了多個億級訪問量系統(tǒng),。工作之余喜歡寫技術(shù)博客,有《跟我學 Spring》,、《跟我學Spring MVC》,、《跟我學Shiro》、《跟我學Nginx+Lua開發(fā)》等系列教程,,目前博客訪問量有460萬+,。
責編:錢曙光,關(guān)注架構(gòu)和算法領(lǐng)域,,尋求報道或者投稿請發(fā)郵件[email protected],,另有「CSDN 高級架構(gòu)師群」,內(nèi)有諸多知名互聯(lián)網(wǎng)公司的大牛架構(gòu)師,,歡迎架構(gòu)師加微信qshuguang2008申請入群,,備注姓名+公司+職位。
簡介最近遇到很多人來咨詢我關(guān)于瀏覽器緩存的一些問題,,而這些問題都是類似的,,因此總結(jié)本文來解答以后遇到類似問題的朋友。 因本文主要以瀏覽器緩存場景介紹,,所以非瀏覽器場景下的一些用法本文不會介紹,,而且本文以chrome為測試瀏覽器。 瀏覽器緩存是指當我們使用瀏覽器訪問一些網(wǎng)站頁面或者http服務(wù)時,,根據(jù)服務(wù)端返回的緩存設(shè)置響應(yīng)頭將響應(yīng)內(nèi)容緩存到瀏覽器,,下次可以直接使用緩存內(nèi)容或者僅需要去服務(wù)端驗證內(nèi)容是否過期即可。這樣的好處可以減少瀏覽器和服務(wù)端之間來回傳輸?shù)臄?shù)據(jù)量,,節(jié)省帶寬提升性能,。 首先看個例子,;當我們第一次訪問http://item.jd.com/1856588.html時將得到如下響應(yīng)頭: 然后接著按F5刷新頁面,將得到如下響應(yīng)頭: 第二次返回的相應(yīng)狀態(tài)碼為304,,表示服務(wù)端文檔沒有修改過,,瀏覽器緩存的內(nèi)容還是最新的。 接下來我們看下如何在Java應(yīng)用層控制瀏覽器緩存,。 示例Last-Modified如下是我們的spring mvc緩存測試代碼: @RequestMapping("/cache")
public ResponseEntity<String> cache(
HttpServletRequest request,
//為了方便測試,,此處傳入文檔最后修改時間
@RequestParam("millis") long lastModifiedMillis,
//瀏覽器驗證文檔內(nèi)容是否修改時傳入的Last-Modified
@RequestHeader (value = "If-Modified-Since", required = false) Date ifModifiedSince) {
//當前系統(tǒng)時間
long now = System.currentTimeMillis();
//文檔可以在瀏覽器端/proxy上緩存多久
long maxAge = 20;
//判斷內(nèi)容是否修改了,此處使用等值判斷
if(ifModifiedSince != null && ifModifiedSince.getTime() == lastModifiedMillis) {
return new ResponseEntity<String>(HttpStatus.NOT_MODIFIED);
}
DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
String body = "<a href=''>點擊訪問當前鏈接</a>";
MultiValueMap<String, String> headers = new HttpHeaders();
//文檔修改時間
headers.add("Last-Modified", gmtDateFormat.format(new Date(lastModifiedMillis)));
//當前系統(tǒng)時間
headers.add("Date", gmtDateFormat.format(new Date(now)));
//過期時間 http 1.0支持
headers.add("Expires", gmtDateFormat.format(new Date(now + maxAge)));
//文檔生存時間 http 1.1支持
headers.add("Cache-Control", "max-age=" + maxAge);
return new ResponseEntity<String>(body, headers, HttpStatus.OK);
}
為了方便測試,,測試時將文檔的修改時間通過millis參數(shù)傳入,,實際應(yīng)用時可以使用如商品的最后修改時間等替代,。 首次訪問 首次訪問http://localhost:9080/cache?millis=1471349916709,,將得到如下響應(yīng)頭: 響應(yīng)狀態(tài)碼200表示請求內(nèi)容成功,另外有如下幾個緩存控制參數(shù): Last-Modified:表示文檔的最后修改時間,,當去服務(wù)器驗證時會拿這個時間去,; Expires:http/1.0規(guī)范定義,表示文檔在瀏覽器中的過期時間,,當緩存的內(nèi)容超過這個時間則需要重新去服務(wù)器獲取最新的內(nèi)容,; Cache-Control:http/1.1規(guī)范定義,表示瀏覽器緩存控制,,max-age=20表示文檔可以在瀏覽器中緩存20秒,。 根據(jù)規(guī)范定義Cache-Control優(yōu)先級高于Expires;實際使用時可以兩個都用,,或僅使用Cache-Control就可以了(比如京東的活動頁sale.jd.com),。一般情況下Expires=當前系統(tǒng)時間(Date) + 緩存時間(Cache-Control: max-age)。大家可以在如上測試代碼進行兩者單獨測試,,緩存都是可行的,。 F5刷新 接著按F5刷新當前頁面,將看到瀏覽器發(fā)送如下請求頭: 此處發(fā)送時有一個If-Modified-Since 請求頭,,其值是上次請求響應(yīng)中的Last-Modified ,,即瀏覽器會拿這個時間去服務(wù)端驗證內(nèi)容是否發(fā)生了變更。接著收到如下響應(yīng)信息: 響應(yīng)狀態(tài)碼為304,,表示服務(wù)端告訴瀏覽器說“瀏覽器你緩存的內(nèi)容沒有變化,,直接使用緩存內(nèi)容展示吧”。
注:在測試時要過一段時間更改下參數(shù)millis來表示內(nèi)容修改了,,要不然會一直看到304響應(yīng),。
Ctrl+F5強制刷新 如果你想強制從服務(wù)端獲取最新的內(nèi)容,可以按Ctrl+F5: 瀏覽器在請求時不會帶上If-Modified-Since ,,并帶上Cache-Control:no-cache 和Pragma:no-cache ,,這是為了告訴服務(wù)端說請給我一份最新的內(nèi)容,。 from cache當我們按F5刷新、Ctrl+F5強制刷新,、地址欄輸入地址刷新時都會去服務(wù)端驗證內(nèi)容是否發(fā)生了變更,。那什么情況才不去服務(wù)端驗證呢?即有些朋友還會發(fā)現(xiàn)有一些“from cache”的情況,,這是什么情況下發(fā)生的呢,? 從A頁面跳轉(zhuǎn)到A頁面或者從A頁面跳轉(zhuǎn)到B頁面時: 大家可以通過如上方式模擬,即從A頁面跳轉(zhuǎn)到A頁面也是情況1,。此時如果內(nèi)容還在緩存時間之內(nèi),,直接從瀏覽器獲取的內(nèi)容,而不去服務(wù)端驗證,。 訪問頁面http://item.jd.com/11056556.html,,然后點擊面包屑中的HTTP權(quán)威指南時會跳轉(zhuǎn)到當前頁面,此時看到如下結(jié)果,,頁面及頁面異步加載的一些js,、css、圖片都from cache了,。 還有如通過瀏覽器歷史記錄進行前進后退時也會走from cache,。本文是基于chrome 52.0.2743.116 m版本測試,不同瀏覽器行為可能存在差異,。 Age 一般用于代理層(如CDN),,大家在訪問京東一些頁面時會發(fā)現(xiàn)有一個Age響應(yīng)頭,然后強制刷新(Ctrl+F5)后會發(fā)現(xiàn)其不斷的變化,;其表示此內(nèi)容在代理層從緩存到現(xiàn)在經(jīng)過了多長時間,,即在代理層緩存了多長時間。 Vary 一般用于代理層(如CDN),,用于代理層和瀏覽器協(xié)商什么情況下使用哪個版本的緩存內(nèi)容(比如壓縮版和非壓縮版),,即什么情況下后續(xù)請求才能使用代理層緩存的該版本內(nèi)容,比如如下響應(yīng)是告知瀏覽器Content-Encoding:gzip ,,即緩存代理層緩存了gzip版本的內(nèi)容,;那么后續(xù)的請求在請求時Accept-Encoding 頭部中包含gzip時才能使用改代理層緩存。 Via 一般用于代理層(如CDN),,表示訪問到最終內(nèi)容經(jīng)過了哪些代理層,、用的什么協(xié)議、代理層是否緩存命中等,;通過它可以進行一些故障診斷,。 ETag@RequestMapping("/cache/etag")
public ResponseEntity<String> cache(
HttpServletRequest request,
HttpServletResponse response,
//瀏覽器驗證文檔內(nèi)容的實體 If-None-Match
@RequestHeader (value = "If-None-Match", required = false) String ifNoneMatch) {
//當前系統(tǒng)時間
long now = System.currentTimeMillis();
//文檔可以在瀏覽器端/proxy上緩存多久
long maxAge = 10;
String body = "<a href=''>點擊訪問當前鏈接</a>";
//弱實體
String etag = "W/\"" + md5(body) + "\"";
if(StringUtils.equals(ifNoneMatch, etag)) {
return new ResponseEntity<String>(HttpStatus.NOT_MODIFIED);
}
DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
MultiValueMap<String, String> headers = new HttpHeaders();
//ETag http 1.1支持
headers.add("ETag", etag);
//當前系統(tǒng)時間
headers.add("Date", gmtDateFormat.format(new Date(now)));
//文檔生存時間 http 1.1支持
headers.add("Cache-Control", "max-age=" + maxAge);
return new ResponseEntity<String>(body, headers, HttpStatus.OK);
}
其中ETag用于發(fā)送到服務(wù)端進行內(nèi)容變更驗證的,而Catch-Control 是用于控制緩存時間的(瀏覽器,、代理層等),。此處我們使用了弱實體W\”343sda”,,弱實體(”343sda”)只要內(nèi)容語義沒變即可,比如內(nèi)容的gzip版和非gzip版可以使用弱實體驗證,;而強實體指字節(jié)必須完全一致(gzip和非gzip情況是不一樣的),,因此建議首先選擇使用弱實體。nginx在生成etag時使用的算法是Last-Modified + Content-Length 計算的: ngx_sprintf(etag->value.data,"\"%xT-%xO\"",
r->headers_out.last_modified_time,
r->headers_out.content_length_n)
到此簡單的基于文檔修改時間和過期時間的緩存控制就介紹完了,,在內(nèi)容型響應(yīng)我們大多數(shù)根據(jù)內(nèi)容的修改時間來進行緩存控制,,ETag根據(jù)實際需求而定(比如)。另外還可以使用html Meta標簽控制瀏覽器緩存,,但是對代理層緩存無效,,因此不建議使用。 總結(jié)
服務(wù)端響應(yīng)的Last-Modified 會在下次請求時以If-Modified-Since 請求頭帶到服務(wù)端進行文檔是否修改的驗證,,如果沒有修改則返回304,,瀏覽器可以直接使用緩存內(nèi)容;
Cache-Control:max-age 和Expires用于決定瀏覽器端內(nèi)容緩存多久,,即多久過期,,過期后則刪除緩存重新從服務(wù)端獲取最新的;另外可以用于from cache場景,;
http/1.1規(guī)范定義的Cache-Control 優(yōu)先級高于http/1.0規(guī)范定義的Expires;
一般情況下Expires=當前系統(tǒng)時間+緩存時間(Cache-Control:max-age),;
http/1.1規(guī)范定義了ETag來通過文檔摘要的方式控制,。
Last-Modified 與ETag同時使用時,瀏覽器在驗證時會同時發(fā)送If-Modified-Since 和If-None-Match ,,按照http/1.1規(guī)范,,如果同時使用If-Modified-Since 和If-None-Match 則服務(wù)端必須兩個都驗證通過后才能返回304;且nginx就是這樣做的,。因此實際使用時應(yīng)該根據(jù)實際情況選擇,。還有If-Match 和If-Unmodified-Since 本文就不介紹了。
接下來我們看下如何使用nginx進行緩存控制,。 nginx緩存設(shè)置nginx提供了expires,、etag、if-modified-since 指令來進行瀏覽器緩存控制,。 expires假設(shè)我們使用nginx作為靜態(tài)資源服務(wù)器,,此時可以使用expires進行緩存控制。 location /img {
alias /export/img/;
expires 1d;
}
當我們訪問靜態(tài)資源時,,如http://192.168.61.129/img/1.jpg,,將得到類似如下的響應(yīng)頭: 對于靜態(tài)資源會自動添加ETag,可以通過添加“etag off”指令禁止生成ETag,。如果是靜態(tài)文件Last-Modified 是文件的最后修改時間,;Expires是根據(jù)當前服務(wù)端系統(tǒng)時間算出來的,。如上nginx配置的計算邏輯(實際計算邏輯比這個多,具體參考官方文檔): if (expires == NGX_HTTP_EXPIRES_ACCESS ||r->headers_out.last_modified_time == -1) {
max_age = expires_time;
expires_time += now;
}
if-modified-since此指令用于表示nginx如何拿服務(wù)端的Last-Modified和瀏覽器端的If-Modified-Since 時間進行比較,,默認“if_modified_since exact ”表示精確匹配,,也可以使用“if_modified_sincebefore ”表示只要文件的上次修改時間早于或等于瀏覽器短的If-Modified-Since時間,就返回304,。 nginx proxy expires使用nginx作為反向代理時,,請求會先進入nginx,然后nginx將請求轉(zhuǎn)發(fā)給后端應(yīng)用,。如下圖所示: 首先配置upstream: upstream backend_tomcat {
server 192.168.61.1:9080 max_fails=10 fail_timeout=10s weight=5;
}
接著配置location: location = /cache {
proxy_pass http://backend_tomcat/cache$is_args$args;
}
接下來我們可以通過如http://192.168.61.129/cache?millis=1471349916709訪問nginx,,nginx會將請求轉(zhuǎn)發(fā)給后端java應(yīng)用。也就是說nginx只是做了相關(guān)的轉(zhuǎn)發(fā)(負載均衡),,并沒有對請求和響應(yīng)做什么處理,。 假設(shè)對后端返回的過期時間需要調(diào)整,可以添加expires指令到location: location = /cache {
proxy_pass http://backend_tomcat/cache$is_args$args;
expires 5s;
}
然后再請求相關(guān)的URL,,將得到如下響應(yīng): 過期時間相關(guān)的響應(yīng)頭被expires指令更改了,,但是Last-Modified 是沒有變的。 即使我們更改了緩存過期頭,,但nginx本身沒有對這些內(nèi)容做緩存,,每次請求還是要到后端驗證的,假設(shè)在過期時間內(nèi),,這些驗證在nginx這一層驗證就可以了,,不需要到后端驗證,這樣可以減少后端的很大壓力,。即整體流程是:
瀏覽器發(fā)起請求,,首先到nginx,nginx根據(jù)url在nginx本地查找是否有文檔緩存,;
nginx沒有找到本地緩存,,則去后端獲取最新的文檔,并放入到nginx本地緩存中,;返回200狀態(tài)碼和最新的文檔給瀏覽器,;
nginx找到本地緩存了,首先驗證文檔是否過期(Cache-Control:max-age=5 ),,如果過期則去后端獲取最新的文檔,,并放入nginx本地緩存中,返回200狀態(tài)碼和最新的文檔給瀏覽器,;如果文檔沒有過期,,如果If-Modified-Since 與緩存文檔的Last-Modified 匹配,則返回300狀態(tài)碼給瀏覽器,,否則返回200狀態(tài)碼和最新的文檔給瀏覽器,。
即內(nèi)容不需要動態(tài)(計算,、渲染等)速度更快,內(nèi)容越接近于用戶速度越快,。像apache traffic server,、squid、varnish,、nginx等技術(shù)都可以來進行內(nèi)容緩存,。還有CDN就是用來加速用戶訪問的: 即用戶首先訪問到全國各地的CDN節(jié)點(使用如ATS、Squid實現(xiàn)),,如果CDN沒命中,,會回源到中央nginx集群,該集群如果沒有命中緩存(該集群的緩存不是必須的,,要根據(jù)實際命中情況等決定),,最后回源到后端應(yīng)用集群。 像我們商品詳情頁的一些服務(wù)就大量使用了nginx緩存減少回源到后端的請求量,,從而提升訪問速度,。可以參考《構(gòu)建需求響應(yīng)式億級商品詳情頁》,、《京東商品詳情頁服務(wù)閉環(huán)實踐》和《應(yīng)用多級緩存模式支撐海量讀服務(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)建兩級目錄結(jié)構(gòu),比如/export/cache/proxy_cache/7/3c/,,將所有文件放在一級目錄結(jié)構(gòu)中如果文件量很大會導(dǎo)致訪問文件慢,;
keys_zone=cache:512m :設(shè)置存儲所有緩存key和相關(guān)信息的共享內(nèi)存區(qū),1M大約能存儲8000個key,;
inactive=5m :inactive指定被緩存的內(nèi)容多久不被訪問將從緩存中移除,以保證內(nèi)容的新鮮,;默認10分鐘,;
max_size=8g :最大緩存閥值,“cachemanager”進程會監(jiān)控最大緩存大小,,當緩存達到該閥值,,該進程將從緩存中移除最近最少使用的內(nèi)容;
use_temp_path :如果為on,,則內(nèi)容首先被寫入臨時文件(proxy_temp_path ),,然后重命名到proxy_cache_path 指定的目錄;如果設(shè)置為off,,則內(nèi)容直接被寫入到proxy_cache_path 指定的目錄,,如果需要cache建議off,該特性是1.7.10提供的,。
location配置location = /cache {
proxy_cache cache;
proxy_cache_key $scheme$proxy_host$request_uri;
proxy_cache_valid 200 5s;
proxy_pass http://backend_tomcat/cache$is_args$args;
add_header cache-status $upstream_cache_status;
}
緩存相關(guān)配置: proxy_cache :指定使用哪個共享內(nèi)存區(qū)域存儲緩存鍵和相關(guān)信息,;
proxy_cache_key :設(shè)置緩存使用的key,,默認為訪問的完整URL,根據(jù)實際情況設(shè)置緩存key,;
proxy_cache_valid :為不同的響應(yīng)狀態(tài)碼設(shè)置緩存時間,;如果是proxy_cache_valid 5s則200、301,、302響應(yīng)將被緩存,。
proxy_cache_valid
proxy_cache_valid 不是唯一設(shè)置緩存時間的,還可以通過如下方式(優(yōu)先級從上到下):
以秒為單位的“X-Accel-Expires ”響應(yīng)頭來設(shè)置響應(yīng)緩存時間,;
如果沒有“X-Accel-Expires ”,,可以根據(jù)“Cache-Control ”、“Expires ”來設(shè)置響應(yīng)緩存時間,;
否則使用proxy_cache_valid 設(shè)置的緩存時間,;
如果響應(yīng)頭包含Cache-Control:private/no-cache/no-store 、Set-Cookie 或者只有一個Vary響應(yīng)頭且其值為*,,則響應(yīng)內(nèi)容將不會被緩存,。可以使用proxy_ignore_headers 來忽略這些響應(yīng)頭,。 add_headercache-status $upstream_cache_status 在響應(yīng)頭中添加緩存命中的狀態(tài):
HIT:緩存命中了,,直接返回緩存中內(nèi)容,不回源到后端,; MISS:緩存沒有命中,,回源到后端獲取最新的內(nèi)容; EXPIRED:緩存命中但過期了,,回源到后端獲取最新的內(nèi)容,; UPDATING:緩存已過期但正在被別的nginx進程更新;配置了proxy_cache_use_staleupdating 指令時會存在該狀態(tài),; STALE:緩存已過期,,但因后端服務(wù)出現(xiàn)了問題(比如后端服務(wù)掛了)返回過期的響應(yīng);配置了如proxy_cache_use_stale error timeout指令后會存在該狀態(tài),; REVALIDATED:啟用proxy_cache_revalidate 指令后,,當緩存內(nèi)容過期時nginx通過一次If-Modified-Since的請求頭去驗證緩存內(nèi)容是否過期,此時會返回該狀態(tài),; BYPASS:proxy_cache_bypass 指令有效時強制回源到后端獲取內(nèi)容,,即使已經(jīng)緩存了。 proxy_cache_min_uses
用于控制請求多少次后響應(yīng)才被緩存,;默認“proxy_cache_min_uses 1;”,,如果緩存熱點比較集中、存儲有限,可以考慮修改該參數(shù)以減少緩存數(shù)量和寫磁盤次數(shù),。 proxy_no_cache
用于控制什么情況下響應(yīng)將不被緩存,;比如配置“proxy_no_cache $args_nocache ”,如果帶的參數(shù)值至少有一個不為空或者0,,則響應(yīng)將不被緩存,。 proxy_cache_bypass
類似于proxy_no_cache ,但是其控制什么情況不從緩存中獲取內(nèi)容,,而是直接到后端獲取內(nèi)容,;如果命中則$upstream_cache_status為BYPASS 。 proxy_cache_use_stale
當對緩存內(nèi)容的過期時間不敏感,,或者后端服務(wù)出問題時即使緩存的內(nèi)容不新鮮也總比返回錯誤給用戶強(類似于托底),,此時可以配置該參數(shù),如“proxy_cache_use_stale error timeout http_500 http_502 http_503http_504 ”:即如果超時,、后端連接出錯,、500、502,、503等錯誤時即使緩存內(nèi)容已過期也先返回給用戶,,此時$upstream_cache_status 為STALE;還有一個updating表示緩存已過期但正在被別的nginx進程更新將先返回過期的內(nèi)容,,此時$upstream_cache_status 為UPDATING,。 proxy_cache_revalidate
當緩存過期后,如果開啟了proxy_cache_revalidate ,,則會發(fā)出一次If-Modified-Since 和If-None-Match 條件請求,,如果后端返回304則會得到兩個好處:節(jié)省帶寬和減少寫磁盤的次數(shù);此時$upstream_cache_status 為REVALIDATED,。 proxy_cache_lock
當多個客戶端同時請求同一份內(nèi)容時,,如果開啟proxy_cache_lock (默認off)則只有一個請求被發(fā)送至后端;其他請求將等待該內(nèi)容返回,;當?shù)谝粋€請求返回時,,其他請求將從緩存中獲取內(nèi)容返回;當?shù)谝粋€請求超過了proxy_cache_lock_timeout 超時時間(默認5s),,則其他請求將同時請求到后端來獲取響應(yīng),且響應(yīng)不會被緩存(在1.7.8版本之前是被緩存的),;啟用proxy_cache_lock 可以應(yīng)對Dog-pile effect (當某個緩存失效時,,同時又大量相同的請求沒命中緩存,而同時請求到后端,,從而導(dǎo)致后端壓力太大,,此時限制一個請求去拿即可)。 proxy_cache_lock_age 是1.7.8新添加的,如果在proxy_cache_lock_age 指定的時間內(nèi)(默認5s),,最后一個發(fā)送到后端進行新緩存構(gòu)建的請求還沒有完成,,則下一個請求將被發(fā)送到后端來構(gòu)建緩存(因為1.7.8版本之后,proxy_cache_lock_timeout 超時之后返回的內(nèi)容是不緩存的,,需要下一次請求來構(gòu)建響應(yīng)緩存),。
清理緩存有時候緩存的內(nèi)容是錯誤的,需要手工清理,,nginx plus版本提供了purger的功能,,但是對于非plus版本的nginx可以考慮使用ngx_cache_purge (https://github.com/FRiCKLE/ngx_cache_purge)模塊進行清理緩存,如: location ~ /purge(/.*) {
allow 127.0.0.1;
deny all;
proxy_cache_purge cache$1$is_args$args;
}
注意該方法應(yīng)該只允許內(nèi)網(wǎng)可以訪問,,如有必要可以考慮需要密碼才能訪問,。
到此代理層緩存就介紹完了,通過代理層緩存可以解決很多問題,,可以參考《京東商品詳情頁服務(wù)閉環(huán)實踐》和《京東商品詳情頁服務(wù)閉環(huán)實踐》,。 一些經(jīng)驗
只緩存200狀態(tài)碼的響應(yīng),像302等要根據(jù)實際場景決定(比如當系統(tǒng)出錯時自動302到錯誤頁面,,此時緩存302就不對了),;
有些頁面不需要強一致,可以進行幾秒的緩存(比如商品詳情頁展示的庫存,,可以緩存幾秒鐘,,短時間的不一致對于用戶來說是沒有影響的);
js/css/image等一些內(nèi)容緩存時間可以設(shè)置的很久(比如1個月甚至1年),,通過在頁面修改版本來控制過期,,不建議隨機數(shù)方式;
假設(shè)商品詳情頁異步加載的一些數(shù)據(jù)使用的是Last-Modified進行的過期控制,,而服務(wù)端做了邏輯修改但內(nèi)容是沒有修改的,,即內(nèi)容的最后修改時間沒變,如果想過期這些異步加載的數(shù)據(jù),,可以考慮在商品詳情頁添加異步加載數(shù)據(jù)的版本號,,通過添加版本號來加載最新的數(shù)據(jù),或者將Last-Modified時間加1來解決,;而這種情況比較適合使用ETag,;
商品詳情頁異步加載的一些數(shù)據(jù),可以考慮更長時間的緩存(比如1個月而不是幾分鐘),,可以通過MQ將修改時間推送商品詳情頁,,從而實現(xiàn)按需過期數(shù)據(jù);
服務(wù)端考慮使用內(nèi)存緩存(tmpfs),、SSD緩存,;考慮服務(wù)端負載均衡算法,,如一致性哈希提升緩存命中率;
緩存KEY要合理設(shè)計(比如去掉參數(shù)/排序參數(shù)保證代理層緩存命中),,要有清理緩存的工具,,出問題時能快速清理掉問題KEY;
AB測試/個性化需求時應(yīng)禁用掉瀏覽器緩存,,但考慮服務(wù)端緩存,;
為了便于查找問題,一般會在響應(yīng)頭中添加源服務(wù)器信息,,如訪問京東商品詳情頁會看到ser響應(yīng)頭,,此頭存儲了源服務(wù)器IP,以便出現(xiàn)問題時知道哪臺服務(wù)器有問題,。
相關(guān)閱讀:
|