瀏覽器緩存機制淺析


非HTTP協議定義的緩存機制

  瀏覽器緩存機制,其實主要就是HTTP協議定義的緩存機制(如: Expires; Cache-control等)。但是也有非HTTP協議定義的緩存機制,如使用HTML Meta 標簽,Web開發者可以在HTML頁面的<head>節點中加入<meta>標簽,代碼如下:

<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

  上述代碼的作用是告訴瀏覽器當前頁面不被緩存,每次訪問都需要去服務器拉取。使用上很簡單,但只有部分瀏覽器可以支持,而且所有緩存代理服務器都不支持,因為代理不解析HTML內容本身。下面主要介紹HTTP協議定義的緩存機制

大話瀏覽器緩存

  瀏覽器緩存一直是一個讓人又愛又恨的存在,一方面極大地提升了用戶體驗,而另一方面有時會因為讀取了緩存而展示了“錯誤”的東西,而在開發過程中千方百計地想把緩存禁掉。如果沒聽說過瀏覽器緩存或者不知道瀏覽器緩存的用處,可以先瀏覽一下這篇文章->Web緩存的作用與類型 

  那么瀏覽器緩存機制到底是如何工作的呢?核心就是把緩存的內容保存在了本地,而不用每次都向服務端發送相同的請求,設想下每次都打開相同的頁面,而在第一次打開的同時,將下載的js、css、圖片等“保存”在了本地,而之后的請求每次都在本地讀取,效率是不是高了很多?真正的瀏覽器工作的時候並不是將完整的內容保存在本地,各種瀏覽器都有不同的方式,譬如firefox是一種類似innodb的方式存儲的key value 的模式,在地址欄中輸入 about:cache 可以看見緩存的文件,chrome會把緩存的文件保存在一個叫User Data的文件夾下。但是如果每次都讀取緩存也會存在一定的問題,如果服務端的文件更新了呢?這時服務端就會和客戶端約定一個有效期,譬如說服務端告訴客戶端1天內我服務端的文件不會更新,你就放心地讀取緩存吧,於是在這一天里每次遇到相同的請求客戶端都開心地可以讀取緩存里的文件。但是如果一天過去了,客戶端又要讀取該文件了,發現和服務端約定的有效期過了,於是就會向服務端發送請求,試圖下載一個新的文件,但是很有可能服務端的文件其實並沒有更新,其實還是可以讀取緩存的。這時該怎么判斷服務端的文件有沒有更新呢?有兩種方式,第一種在上一次服務端告訴客戶端約定的有效期的同時,告訴客戶端該文件最后修改的時間,當再次試圖從服務端下載該文件的時候,check下該文件有沒有更新(對比最后修改時間),如果沒有,則讀取緩存;第二種方式是在上一次服務端告訴客戶端約定有效期的同時,同時告訴客戶端該文件的版本號,當服務端文件更新的時候,改變版本號,再次發送請求的時候check一下版本號是否一致就行了,如一致,則可直接讀取緩存。

  而事實上真正的瀏覽器緩存機制大抵也是如此,接下來就可以分別對號入座了。

  需要注意的是,瀏覽器會在第一次請求完服務器后得到響應,我們可以在服務器中設置這些響應,從而達到在以后的請求中盡量減少甚至不從服務器獲取資源的目的。瀏覽器是依靠請求和響應中的的頭信息來控制緩存的

Expires與Cache-Control

  Expires和Cache-Control就是服務端用來約定和客戶端的有效時間的。

  

  比如如上一個響應頭,Expires規定了緩存失效時間(Date為當前時間),而Cache-Control的max-age規定了緩存有效時間(2552s),理論上這兩個值計算出的有效時間應該是相同的(上圖好像不一致)。Expires是HTTP1.0的東西,而Cache-Control是HTTP1.1的,規定如果max-age和Expires同時存在,前者優先級高於后者。Cache-Control的參數可以設置很多值,譬如(參考瀏覽器緩存機制):

2016-07-21 補充:

no-cache 和 no-store

no-cache表示必須先與服務器確認返回的響應是否被更改,然后才能使用該響應來滿足后續對同一個網址的請求。因此,如果存在合適的驗證令牌 (ETag),no-cache 會發起往返通信來驗證緩存的響應,如果資源未被更改,可以避免下載。

相比之下,no-store更加簡單,直接禁止瀏覽器和所有中繼緩存存儲返回的任何版本的響應 - 例如:一個包含個人隱私數據或銀行數據的響應。每次用戶請求該資源時,都會向服務器發送一個請求,每次都會下載完整的響應。

public和 private

如果響應被標記為public,即使有關聯的 HTTP 認證,甚至響應狀態碼無法正常緩存,響應也可以被緩存。大多數情況下,public不是必須的,因為明確的緩存信息(例如max-age)已表示 響應可以被緩存。

相比之下,瀏覽器可以緩存private響應,但是通常只為單個用戶緩存,因此,不允許任何中繼緩存對其進行緩存 - 例如,用戶瀏覽器可以緩存包含用戶私人信息的 HTML 網頁,但是 CDN 不能緩存。

max-age

該指令指定從當前請求開始,允許獲取的響應被重用的最長時間(單位為秒) - 例如:max-age=60表示響應可以再緩存和重用 60 秒。

Last-Modified/If-Modified-Since

  而Last-Modified/If-Modified-Since就是上面說的當有效期過后,check服務端文件是否更新的第一種方式,要配合Cache-Control使用。比如第一次訪問我的主頁simplify the life,會請求一個jquery文件,響應頭返回如下信息:

  然后我在主頁按下ctrl+r刷新,因為ctrl+r會默認跳過max-age和Expires的檢驗直接去向服務器發送請求(下文再探討各種刷新后如何讀取緩存),我們看看請求截圖:

  請求頭中包含了If-Modified-Since項,而它的值和上次請求響應頭中的Last-Modified一致,我們發現這個日期是在遙遠的2013年,也就是說這個jquery文件自從2013年的那個日期后就沒有再被修改過了。將If-Modified-Since的日期和服務端該文件的最后修改日期對比,如果相同,則響應HTTP304,從緩存讀數據;如果不相同文件更新了,HTTP200,返回數據,同時通過響應頭更新last-Modified的值(以備下次對比)。

ETag/If-None-Match

   而ETag/If-None-Match則是上文大話中說的第二種check服務端文件是否更新的方式,也要配合Cache-Control使用。實際上ETag並不是文件的版本號,而是一串可以代表該文件唯一的字符串(Apache中,ETag的值,默認是對文件的索引節(INode),大小(Size)和最后修改時間(MTime)進行Hash后得到的。),當客戶端發現和服務器約定的直接讀取緩存的時間過了,就在請求中發送If-None-Match選項,值即為上次請求后響應頭的ETag值,該值在服務端和服務端代表該文件唯一的字符串對比(如果服務端該文件改變了,該值就會變),如果相同,則相應HTTP304,客戶端直接讀取緩存,如果不相同,HTTP200,下載正確的數據,更新ETag值。

  看如上截圖,與服務器約定的直接讀取本地緩存的時間過了,就會向服務器發送新的請求,請求頭中帶If-None-Match項,該字符串值會在服務端進行匹配,很顯然,並沒有什么變化(看響應頭的ETag值),於是響應HTTP304,直接讀取緩存。或許你會發送該請求也有If-Modified-Since項,如果兩者同時存在,If-None-Match優先,忽略If-Modified-Since。或許你會問為什么它優先?兩者功能相似甚至相同,為什么要同時存在?HTTP1.1中ETag的出現主要是為了解決幾個Last-Modified比較難解決的問題:

  1.  Last-Modified標注的最后修改只能精確到秒級,如果某些文件在1秒鍾以內,被修改多次的話,它將不能准確標注文件的修改時間
  2. 如果某些文件會被定期生成,但有時內容並沒有任何變化(僅僅改變了時間),但Last-Modified卻改變了,導致文件沒法使用緩存
  3. 有可能存在服務器沒有准確獲取文件修改時間,或者與代理服務器時間不一致等情形

不能緩存的請求

  當然並不是所有請求都能被緩存。

  無法被瀏覽器緩存的請求:

  1. HTTP信息頭中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告訴瀏覽器不用緩存的請求
  2. 需要根據Cookie,認證信息等決定輸入內容的動態請求是不能被緩存的
  3. 經過HTTPS安全加密的請求(有人也經過測試發現,ie其實在頭部加入Cache-Control:max-age信息,firefox在頭部加入Cache-Control:Public之后,能夠對HTTPS的資源進行緩存,參考《HTTPS的七個誤解》)
  4. POST請求無法被緩存
  5. HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求無法被緩存

用戶行為與緩存

  瀏覽器緩存過程還和用戶行為有關,譬如上面提到的,打開我的主頁simplify the life,有個jquery的請求,如果直接在地址欄按回車,響應HTTP200(from cache),因為有效期還沒過直接讀取的緩存;如果ctrl+r進行刷新,則會相應HTTP304(Not Modified),雖然還是讀取的本地緩存,但是多了一次服務端的請求;而如果是ctrl+shift+r強刷,則會直接從服務器下載新的文件,響應HTTP200。

  通過上表我們可以看到,當用戶在按F5進行刷新的時候,會忽略Expires/Cache-Control的設置,會再次發送請求去服務器請求,而Last-Modified/Etag還是有效的,服務器會根據情況判斷返回304還是200;而當用戶使用Ctrl+F5進行強制刷新的時候,只是所有的緩存機制都將失效,重新從服務器拉去資源。

  更多可以參考瀏覽器緩存機制

總結

  盜圖瀏覽器緩存機制,兩張圖很清晰

 

 

參考

  1.  再記:瀏覽器緩存200(from cache)和304小結
  2. 【Web緩存機制系列】2 – Web瀏覽器的緩存機制 
  3. 瀏覽器緩存機制-吳秦
  4. 瀏覽器緩存機制
  5. 初探 HTTP 1.1 Cache 機制
  6. HTTP 緩存

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM