最近惡補http基礎,看到緩存章節,而後前段時間又正好讀到流雲諸葛博客裏一篇介紹瀏覽器緩存的文章,二者結合,受益不淺。本身也半抄半總結的記錄一下。html
瀏覽器緩存分爲強緩存和協商緩存。當客戶端請求某個資源時,獲取緩存的流程以下:chrome
先根據這個資源的一些http header判斷它是否命中強緩存(後文再解釋什麼是強緩存),若是命中(cache hit),則直接從本地獲取緩存資源,不會發請求到服務器;瀏覽器
當強緩存沒有命中時,客戶端會發送請求到服務器,服務器經過另外一些request header驗證這個資源是否命中協商緩存(後文再解釋什麼是協商緩存),稱爲http再驗證(revalidation),若是命中(revalidate hit再驗證命中),服務器將請求返回,但不返回資源,而是告訴客戶端直接從緩存中獲取,客戶端收到返回後就會從緩存中獲取資源;緩存
強緩存和協商緩存共同之處在於,若是命中緩存,服務器都不會返回資源;區別是,強緩存不對發送請求到服務器,但協商緩存會。服務器
當協商緩存也沒命中時,服務器就會將資源發送回客戶端。併發
直白點的方式描述下:客戶端第一次問服務器要某個資源時,服務器丟還給客戶端所請求的這個資源同時,告訴客戶端將這個資源保存在本地,而且在將來的某個時點以前若是還須要這個資源,直接從本地獲取就好了,不用向服務器請求。這種方式緩存下來的資源稱爲強緩存。負載均衡
強緩存利用http的返回頭部的中Expires(實體首部字段)或者Cache-Control(通用首部字段)兩個字段來控制的,用來表示資源的緩存時間。服務器經過這兩個首部字段告知客戶端資源的緩存過時時間和緩存最大生命週期。客戶端得知資源的緩存過時時間和最大生命週期後,便可自行判斷是否可不創建與服務器的連接,直接從瀏覽器緩存中獲取資源。分佈式
命中強緩存時,瀏覽器一樣會受到status=200的response,chrome中可經過size區分從服務器返回的資源仍是強緩存得到的資源。函數
該字段是http1.0時的規範,值爲一個絕對時間的GMT格式的時間字符串,表明緩存資源的過時時間,在這個時點以前,即命中緩存。其過程以下:編碼
瀏覽器第一次跟服務器請求一個資源時,服務器在返回這個資源時,在相應頭部會加上Expires,如圖:
瀏覽器接收到該資源後,會把這個資源連同response header一塊兒緩存下來;
瀏覽器再次請求這個資源時,會先從緩存中找到這個資源,而後獲取Expires時間與當前的請求時間比較,若是Expires時間比當前瀏覽器的請求時間晚,說明緩存未過時,即命中緩存;
若是當前請求時間比Expires晚,說明緩存過時,即未命中緩存,瀏覽器就會發送請求到服務器申請獲取資源。
缺點:服務器返回的Expires時點是服務器上的時間,可能與客戶端有時間差,時間差太大時可能形成緩存混亂。
該字段是http1.1的規範,強緩存利用其max-age值來判斷緩存資源的最大生命週期,它的值單位爲秒,Cache-Control : max-age=3600表明緩存資源有效時間爲1小時,即從第一次獲取該資源起一小時內的請求都被認爲可命中強緩存。其過程以下:
瀏覽器第一次跟服務器請求一個資源時,服務器在返回這個資源時,在相應頭部會加上Cache-Control:max-age=XXXXXXXX,如圖:
瀏覽器接收到資源後,連同response header一塊兒緩存下來;
瀏覽器再次跟服務器請求同一個資源時,會先從緩存中找到這個資源,獲取date(第一次資源返回的響應時間)和max-age並計算出一個有效期(date + max-age),而後再和瀏覽器請求時間比較最後判斷是否命中緩存(邏輯同Expires);
若是沒有命中緩存,瀏覽器直接從服務器請求資源,Cache-Control會在從新獲取到服務器返回資源時更新。
Cache-Control描述的是相對時間,採用本地時間來計算資源的有效期,因此相比Expires更可靠。
這兩個Header能夠只用其一,也能夠一塊兒使用。一塊兒使用時以Cache-Control爲準。
直白點的方式描述下:客戶端第一次問服務器要某個資源時,服務器丟還給客戶端所請求的這個資源同時,將該資源的一些信息(文件摘要、或者最後修改時間)也返回給客戶端,告訴客戶端將這個資源緩存在本地。當客戶端下一次須要這個資源時,將請求以及相關信息(文件摘要、或者最後修改時間)一併發送給服務器,由服務器來判斷客戶端緩存的資源是否須要更新:如不須要更新,就直接告訴客戶端獲取本地緩存資源;如須要更新,則將最新的資源連同相應的信息一併返回給客戶端。
當強緩存未命中時,瀏覽器就會發送請求到服務器,服務器會驗證協商緩存是否命中,若是協商緩存命中,請求返回的http狀態爲304,並會顯示說明Not Modified,瀏覽器收到該返回後,就會從緩存中加載了。
協商緩存利用[Last-Modified , If-Modified-Since] 和 [ETag , If-None-Match]這兩對Header來管理。
Last-Modified爲實體首部字段,值爲資源最後更新時間,隨服務器response返回。
If-Modified-Since爲請求首部字段,經過比較兩個時間來判斷資源在兩次請求期間是否有過修改,若是沒有修改,則命中協商緩存,瀏覽器從緩存中獲取資源;若是有過修改,則服務器返回資源,同時返回新的Last-Modified時間。其過程以下:
瀏覽器第一次請求服務器資源,且服務器返回了該資源時,會在response headers中加上Last-Modified,這個header表示這個資源在服務器上的最後一次修改時間;
當瀏覽器再次請求該資源時,會在request headers中加上If-Modified-Since,這個值即爲上一次服務器返回的Last-Modified時間;
服務器再次收到資源請求時,將If-Modified-Since時間和資源在服務器上的最後修改時間與對比,若是If-Modifid-Since與最後修改時間一致,則命中緩存,服務器返回304,瀏覽器從緩存中獲取資源;若未命中緩存,服務器返回資源同時,瀏覽器緩存資源的Last-Modified會被更新。
有些狀況下僅判斷最後修改日期來驗證資源是否有改動是不夠的:
存在週期性重寫某些資源,但資源實際包含的內容並沒有變化;
被修改的信息並不重要,如註釋等;
Last-Modified沒法精確到毫秒,但有些資源更新頻率有時會小於一秒。
爲解決這些問題,http容許用戶對資源打上標籤(ETag)來區分兩個相同路徑獲取的資源內容是否一致。一般會採用MD5等密碼散列函數對資源編碼獲得標籤(強驗證器);或者經過版本號等方式,如W/」v1.0」(W/表示弱驗證器)。
ETag爲相應頭部字段,表示資源內容的惟一標識,隨服務器response返回;
If-None-Match爲請求頭部字段,服務器經過比較請求頭部的If-None-Match與當前資源的ETag是否一致來判斷資源是否在兩次請求之間有過修改,若是沒有修改,則命中協商緩存,瀏覽器從緩存中獲取資源;若是有過修改,則服務器返回資源,同時返回新的ETag。其過程以下:
服務器第一次收到瀏覽器發出的資源請求時,會在response headers中加上ETag,這個ETag是根據該資源生成的惟一標識,這個惟一標識是個字符串,只要服務器認爲資源有變化且應該提供新的資源,則ETag就必須有變化。瀏覽器將資源連同ETag一併緩存。
當瀏覽器再一次向服務器發送該資源的請求時,會在request headers中加上If-None-Match,該值即爲第一次服務器返回的ETag值;
服務器收到資源請求後,會根據要請求的資源從新計算生成相應的ETag,而後與If-None-Match比較。對比結果一致即命中緩存,不一致則未命中緩存,返回資源同時將新的ETag發送至瀏覽器。
[Last-Modified , If-Modified-Since]和[ETag , If-None-Match]通常同時啓用,這是爲了處理Last-Modified不可靠的狀況。
分佈式系統裏多臺服務器間的文件的Last-Modified必須保持一致,以避免負載均衡到不一樣服務器致使對比結果不一致;
分佈式系統儘可能關閉掉ETag(每臺機器生成的ETag都會不同,淘寶頁面中的靜態資源response headers中都沒有ETag)。