最近在做項目,測試同學,客戶總是刷新刷不到最新的頁面,需要按著Ctrl+F5才能看到最新的頁面。之前知道304的概念,去看了測試同學的電腦上面有的是304,有的是內(nèi)存緩存,有的是磁盤緩存。順著這個去深入研究了一下。這里可以分為強緩存和協(xié)商緩存。

盜了一張圖

瀏覽器第一次打開網(wǎng)頁的獲取資源后,根據(jù)返回的響應頭信息告訴客戶端應該如何做緩存。
一、304資源離線緩存
1. 關于Last-Modified
瀏覽器第一次請求到頁面的時候,服務器返回狀態(tài)200,資源相應頭里面有一個Last-Modified的屬性標識,意味著這個文件在服務器端最后一次修改時間。其次還有一個叫做Etag的,他們格式如下
Last-Modified這里是一個GMT的時間標準和我們的北京時間相差8小時
客戶端第二次請求此URL時,根據(jù) HTTP 協(xié)議的規(guī)定,瀏覽器會向服務器傳送 If-Modified-Since和If-None-Match(可選報頭,值Etag的值) 報頭,詢問該時間之后文件是否有被修改過

如果服務器端的資源沒有變化,則自動返回 HTTP 304 (Not Changed.)狀態(tài)碼,內(nèi)容為空,否則重新發(fā)起請求,請求下載資源這樣就節(jié)省了傳輸數(shù)據(jù)量。當服務器端代碼發(fā)生改變或者重啟服務器時,則重新發(fā)出資源,返回和第一次請求時類似。從而保證不向客戶端重復發(fā)出資源,也保證當服務器有變化時,客戶端能夠得到最新的資源。

只有get會被緩存post不會
2. 什么是”Etag”?
HTTP 協(xié)議規(guī)格說明定義ETag為“被請求變量的實體值” 。 另一種說法是,ETag是一個可以與Web資源關聯(lián)的記號(token)。典型的Web資源可以一個Web頁,但也可能是JSON或XML文檔。服務器單獨負責判斷記號是什么及其含義,并在HTTP響應頭中將其傳送到客戶端,以下是服務器端返回的格式:
ETag: "50b1c1d4f775c61:df3"
客戶端的查詢更新格式是這樣的:
If-None-Match: W/"50b1c1d4f775c61:df3"
如果ETag沒改變,則返回狀態(tài)304然后不返回,這也和Last-Modified一樣。本人測試Etag主要在斷點下載時比較有用。
ETag出現(xiàn)的歷史原因是Last-Modified只做到了 “秒級別”的驗證,無法辨識毫秒,微妙級別的校驗,因此才出現(xiàn)了ETag。
ETag的出現(xiàn),意味著客戶端需要需求的升級,因此If-None-Match,If-Match,If-Range隨之用來驗證文件變化。
3.Last-Modified和Etags可以提高性能
聰明的開發(fā)者會把Last-Modified 和ETags請求的http報頭一起使用,這樣可利用客戶端(例如瀏覽器)的緩存。因為服務器首先產(chǎn)生 Last-Modified/Etag標記,服務器可在稍后使用它來判斷頁面是否已經(jīng)被修改。本質(zhì)上,客戶端通過將該記號傳回服務器要求服務器驗證其(客戶端)緩存。
過程如下:
客戶端請求一個頁面(A)。
服務器返回頁面A,并在給A加上一個Last-Modified/ETag。
客戶端展現(xiàn)該頁面,并將頁面連同Last-Modified/ETag一起緩存。
客戶再次請求頁面A,并將上次請求時服務器返回的Last-Modified/ETag一起傳遞給服務器。
服務器檢查該Last-Modified或ETag,并判斷出該頁面自上次客戶端請求之后還未被修改,直接返回響應304和一個空的響應體。
此外,如果緩存服務器版本,類型不同,建議使用Last-Modified,Etag可能造成緩存無法驗證的問題
二、緩存有效期
HTTP中,通過Cache-Control首部和Expires首部為文檔指定了過期時間,通過對過期時間的判斷,緩存就可以知道文檔是不是在保質(zhì)期內(nèi)。Expires首部和Cache-Control:max-age首部都是來告訴緩存文檔有沒有過期,為什么需要兩個響應首部來做這件簡單的事情了?其實這一切都是歷史原因,Expires首部是HTTP 1.0中提出來的,因為他使用的是絕對日期,如果服務端和客戶端時鐘不同步的話(實際上這種情況非常常見),緩存可能就會認為文檔已經(jīng)過了保質(zhì)期。
HTTP 1.1為了修正這個問題,引入了Cache-Control:max-age首部,這個首部使用相對時間來控制保質(zhì)期,讓一切變得更加合理。舉個例子,我們買了一瓶汽水,如果使用Expires首部來標注保質(zhì)期,就會這么寫:飲料過期時間:2012年12月21日,如果某個2貨不知道今天多少號,他還真不知道這飲料過期沒,我小時候飲料都這么寫。后來,有個挺有名的賣牛奶的,大概就叫蒙牛,他發(fā)明了一種標注保質(zhì)期的方法,他怎么搞了?他這么寫:保質(zhì)期:12個月,行,牛逼了,我牛奶一年前就生產(chǎn)出來的牛奶,今天要發(fā)給廠家,發(fā)之前,先往包裝上印上生產(chǎn)日期(當然是印發(fā)貨那天),然后告訴你,明年才過期,這多聰明,搞成相對的,毒死你。也許HTTP 1.1借鑒了這個偉大的發(fā)明,于是就有了Cache-Control:max-age首部。
1. Last-Modified&Cache-Control 與 Expire
《1》Last-Modified & Cache-Control
設置 header("Last-Modified: ".gmdate("D, d M Y H:i:s", time() )." GMT");
Last-Modified雖然使用了緩存,但是每次打開頁面依然需要向服務器發(fā)起http請求,瀏覽器根據(jù)用戶的$_SERVER['HTTP_IF_MODIFIED_SINCE']來判斷瀏覽器的內(nèi)容是否過期,沒過期的話返回304狀態(tài),瀏覽器內(nèi)容從緩存中讀取。
此外 Cache-Control也很重要,如果他的值是max-age=0,max-stable,min-refresh等于0或no-store之類的,緩存是不會被緩存的,每次都會請求服務器。如果是max-age,max-stable,min-refresh[大于0]或only-if-cached,immutable,那么很可能出現(xiàn) from cache現(xiàn)象。
《2》Expires緩存控制
設置 header("Expires: ".gmdate("D, d M Y H:i:s", time()+$cache_time )." GMT");
狀態(tài)碼依然是200,時間依然是舊的時間,Size欄目顯示為from cache,表示內(nèi)容是直接從瀏覽器讀取,瀏覽器根本就沒有向服務器發(fā)起http請求。


這里有可以分為磁盤緩存和內(nèi)存緩存區(qū)別是
內(nèi)存中的緩存就是資源存放在內(nèi)存中,如果瀏覽器關閉了那么內(nèi)存中的資源就會被釋放,所以它是一個短期緩存,它只會在下一個導航前保存這些緩存,有些情況下可能更短。當訪問過頁面后,有的數(shù)據(jù)和資源就會被放入內(nèi)存中,比如:圖片、樣式、腳本等。 內(nèi)存中的緩存讀取數(shù)據(jù)比磁盤快,所以像樣式和圖片這類的資源會優(yōu)先使用memory cache,這樣讀取速度快頁面加載就不會容易卡頓。
磁盤緩存就是將資源緩存在磁盤中,相比于內(nèi)存緩存memory cache磁盤緩存容量就大得多了,但是讀取速度會慢一點;所有的資源數(shù)據(jù)可以緩存,尤其是一些大文件,所以會看到有些腳本文件會存放在內(nèi)存中,有的會存放在磁盤中,內(nèi)存容量有限放不下的數(shù)據(jù)就會存入磁盤中。
磁盤緩存和內(nèi)存緩存其實都是瀏覽器強緩存策略的方式,
2. 200 from Cache實現(xiàn)
Status 200 Ok (from cache)出現(xiàn)的條件是Cache-Control或者Expires滿足一定的條件。
注意:緩存控制是服務器進行報頭建議,以下報頭是響應頭,不是請求頭
2.1 使用 Cache-Control
1.[(max-age|max-stable|min-refresh) = 緩存創(chuàng)建時間 < 當前系統(tǒng)時間][immutable][only-if-cached]
2.緩存必須帶有ETag或者Last-Modified
Cache-Control:public,Max-Age=84800
ETag:"f6c01531e9c65fa96f3d40409fd030f1"

2.2 Expires不能過期
Expires:Sun, 31 Jul 2016 00:19:47 GMT

以上2種方案只要實現(xiàn)一種即可實現(xiàn)資源from cache
對于瀏覽器而言,還有一種數(shù)據(jù)是 DataURL協(xié)議的數(shù)據(jù),這種數(shù)據(jù)也會從緩存讀取,實現(xiàn)from cache ,但是,如果將所有數(shù)據(jù)轉(zhuǎn)碼成DataURL,會出現(xiàn)性能問題。
三.緩存使用技巧
①. 無論是開發(fā)階段還是生產(chǎn)階段,建議使用Cache-Control + Last-Modified或Tag控制緩存
②. 開發(fā)階段, 建議使用Cache-Control:[no-cache,no-store|Max-Age=0]這樣可以阻止瀏覽器使用緩存
③ 無論是開發(fā)階段還是生產(chǎn)階段,如果是永遠不會被更改的資源,那么建議使用緩存Cache-Control:[Max-Age=3600][only-if-cached][immutable] 從而實現(xiàn)from cache,減少http請求。
④. 如果是生產(chǎn)階段建議使用Cache-Control:Max-Age=3600[no-cache|] ,緩存1小時,每次必須到服務器進行校驗
⑤. 禁止緩存
Cache-Control: no-cache, no-store, must-revalidate
⑥. 緩存靜態(tài)資源也可以加上public,實現(xiàn)跨域緩存共享
Cache-Control:public, max-age=31536000
⑦. must-revalidate,校驗本地緩存是否過期,過期了才去請求服務器更新緩存
⑧ 默認情況下,Cache-Control:public緩存都回去和服務器校驗的
關于 Cache-Control: max-age=秒 和 Expires
Expires = 時間,HTTP 1.0 版本,緩存的載止時間,允許客戶端在這個時間之前不去檢查(發(fā)請求)
max-age = 秒,HTTP 1.1版本,資源在本地緩存多少秒。
如果max-age和Expires同時存在,則被Cache-Control的max-age覆蓋。
Expires 的一個缺點就是,返回的到期時間是服務器端的時間,這樣存在一個問題,如果客戶端的時間與服務器的時間相差很大,那么誤差就很大,所以在HTTP 1.1版開始,使用Cache-Control: max-age=秒替代。
Expires =max-age + “每次下載時的當前的request時間”
所以一旦重新下載的頁面后,expires就重新計算一次,但last-modified不會變化