
先看上圖,如果對圖中的(a)(b)(c)(d)四個過程的處理方式都很清楚了,那么請不用再看本文了。
兩個概念
-
強緩存
用戶發送的請求,直接從客戶端緩存中獲取,不發送請求到服務器,不與服務器發生交互行為。 -
協商緩存
用戶發送的請求,發送到服務器后,由服務器判定是否從緩存中獲取資源。 -
兩者共同點:客戶端獲得的數據最后都是從客戶端緩存中獲得。
-
兩者的區別:從名字就可以看出,強緩存不與服務器交互,而協商緩存則需要與服務器交互。
四個過程詳解
(a)瀏覽器判定是否有緩存
先理解個概念,所謂“客戶端緩存”就是指用戶設備中本地資源。不同瀏覽器緩存文件的地址也不盡相同。
我們以chrome為例來查看下瀏覽器緩存文件的地址,
1)首先在chrome中輸入:chrome://chrome-urls/,看到一堆列表,里面隱藏了許多瀏覽器的奧秘,有興趣的可以自己深扒。
2)找到 chrome://cache(當然也可以直接輸入這個地址)
為了驗證緩存,我們打開百度,打開開發者模式,去掉disable-cache選項

從上圖中可以看到,第一個從緩存中取的文件是: https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/css/super_min_fec0412a.css
然后回到chrome://cache 頁面,找到它,並點擊進去,可以看到:

回到問題,瀏覽器怎么判定是否有緩存,就可以轉化為瀏覽器去讀取本地放緩存的地方(注:不同瀏覽器不同系統都會不同)是否有該對應的請求啦。
總結來說就是個查找文件是否存在的問題。
(b)緩存是否過期

我們再以這張圖為例,這張圖中表明,客戶端保留了一個服務器端的response header。
里面的Date字段表明此次緩存時服務器的時間。
里面有兩個字段:expires 、Cache-Control
- expires
Http1.0 中的標准,表明過期時間,注意此處的時間都是指的是服務器的時間。
可以看到過期時間被設定為了:Thu, 28 Sep 2017 06:38:37 GMT
存在的問題:服務器時間與客戶端時間的不一致,就會導致緩存跟期待效果出現偏差。
- Cache-Control
Http1.1 中的標准,可以看成是 expires 的補充。使用的是相對時間的概念。
簡單介紹下Cache-Control的屬性設置。
1)max-age: 設置緩存的最大的有效時間,單位為秒(s)。max-age會覆蓋掉Expires
- s-maxage: 只用於共享緩存,比如CDN緩存(s -> share)。與max-age 的區別是:max-age用於普通緩存,
而s-maxage用於代理緩存。如果存在s-maxage,則會覆蓋max-age 和 Expires. - public:響應會被緩存,並且在多用戶間共享。默認是public。
- private: 響應只作為私有的緩存,不能在用戶間共享。如果要求HTTP認證,響應會自動設置為private。
5)no-cache: 指定不緩存響應,表明資源不進行緩存。但是設置了no-cache之后並不代表瀏覽器不緩存,而是在緩存前要向服務器確認資源是否被更改。因此有的時候只設置no-cache防止緩存還是不夠保險,還可以加上private指令,將過期時間設為過去的時間。
6)no-store: 絕對禁止緩存。
7)must-revalidate: 如果頁面過期,則去服務器進行獲取。
設置cache-control 的規則可以參見下圖:

所以判斷緩存是否過期步驟是:
1) 查看是否有cache-control 的max-age / s-maxage , 如果有,則用服務器時間date值 + max-age/s-maxage 的秒數計算出新的過期時間,將當前時間與過期時間進行比較,判斷是否過期
2)查看是否有cache-control 的max-age / s-maxage,則用expires 作為過期時間比較

總結:(b)過程執行完后,如果判定為未過期,則使用客戶端緩存。那么就是屬於“強緩存”。
(c)跟服務器協商是否使用緩存

到這一步的時候,瀏覽器會向服務器發送請求,同時如果上一次的緩存中有Last-modified 和 Etag 字段,
瀏覽器將在request header 中加入If-Modified-Since(對應於Last-modified), 和If-None-Match(對應於Etag)。
- Last-modified: 表明請求的資源上次的修改時間。
- If-Modified-Since:客戶端保留的資源上次的修改時間。
- Etag:資源的內容標識。(不唯一,通常為文件的md5或者一段hash值,只要保證寫入和驗證時的方法一致即可)
- If-None-Match: 客戶端保留的資源內容標識。
⚠️:
1) 分布式系統盡量關閉Etag,因為每台機器生成的Etag都不一樣。
2)分布式系統里多台機器間文件的Last-Modified必須一致,以免負載均衡不同導致對比失敗。
通常情況下,如果同時發送 If-None-Match 、If-Modified-Since字段,服務器只要比較etag 的內容即可,當然具體處理方式,看服務器的約定規則。
(d)協商緩存
在這個階段,服務器一般會將Cache-control、expires 、last-modified、date、etag 等字段在response header 中返回,便於下次緩存。當然具體的場景,也是看服務器的約定規則設定。
緩存的不同來源
⚠️: 這個問題暫時沒有找到非常滿意的、清楚的回答。
from disk cache
從磁盤中獲取緩存資源,等待下次訪問時不需要重新下載資源,而直接從磁盤中獲取。它的直接操作對象為CurlCacheManager。
from memory cache
從內存中獲取資源,等待下次訪問時不需要重新下載資源,而直接從內存中獲取。Webkit早已支持memoryCache。
目前Webkit資源分成兩類,一類是主資源,比如HTML頁面,或者下載項,一類是派生資源,比如HTML頁面中內嵌的圖片或者腳本鏈接,分別對應代碼中兩個類: MainResourceLoader和SubresourceLoader。雖然Webkit支持memoryCache,但是也只是針對派生資源,它對應的類為CachedResource,用於保存原始數據(比如CSS,JS等),以及解碼過的圖片數據。
區別
當退出進程時,內存中的數據會被清空,而磁盤的數據不會,所以,當下次再進入該進程時,該進程仍可以從diskCache中獲得數據,而memoryCache則不行。
相似
diskCache與memoryCache相似之處就是也只能存儲一些派生類資源文件。它的存儲形式為一個index.dat文件,記錄存儲數據的url,然后再分別存儲該url的response信息和content內容。Response信息最大作用就是用於判斷服務器上該url的content內容是否被修改。
用戶行為
最后附上一張,用戶行為影響瀏覽器的緩存行為。

總結
自此可以將本文開頭的流程圖理解清楚。
遺留問題
- CDN緩存?
- param: no-cache?
- 分布式系統 ?
- 負載均衡?
- 緩存資源的存儲與讀取,瀏覽器是以什么規則去區分memory cache , 還是disk cache ?
參考文獻
Chrome瀏覽器的緩存文件位置: http://jingyan.baidu.com/article/f3e34a128e41acf5ea653554.html
瀏覽器緩存機制:http://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html
瀏覽器緩存知識小結及應用: http://mp.weixin.qq.com/s/HRrYWnZIWgE_Hawr81CZTw
200 ok 幾種狀態:http://www.cnblogs.com/tangyuu/p/6396644.html
