2021-05-12 14:32:11
瀏覽器快取機制淺析
非HTTP協定定義的快取機制
瀏覽器快取機制,其實主要就是HTTP協定定義的快取機制(如: Expires; Cache-control等)。但是也有非HTTP協定定義的快取機制,如使用HTML Meta 標籤,Web開發者可以在HTML頁面的<head>節點中加入<meta>標籤,程式碼如下:
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
上述程式碼的作用是告訴瀏覽器當前頁面不被快取,每次存取都需要去伺服器拉取。使用上很簡單,但只有部分瀏覽器可以支援,而且所有快取代理伺服器都不支援,因為代理不解析HTML內容本身。下面主要介紹HTTP協定定義的快取機制。
大話瀏覽器快取
瀏覽器快取一直是一個讓人又愛又恨的存在,一方面極大地提升了使用者體驗,而另一方面有時會因為讀取了快取而展示了“錯誤”的東西,而在開發過程中千方百計地想把快取禁掉。
那麼瀏覽器快取機制到底是如何工作的呢?核心就是把快取的內容儲存在了本地,而不用每次都向伺服器端傳送相同的請求,設想下每次都開啟相同的頁面,而在第一次開啟的同時,將下載的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的引數可以設定很多值,譬如(參考瀏覽器快取機制):
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比較難解決的問題:
- Last-Modified標注的最後修改只能精確到秒級,如果某些檔案在1秒鐘以內,被修改多次的話,它將不能準確標註檔案的修改時間
- 如果某些檔案會被定期生成,但有時內容並沒有任何變化(僅僅改變了時間),但Last-Modified卻改變了,導致檔案沒法使用快取
- 有可能存在伺服器沒有準確獲取檔案修改時間,或者與代理伺服器時間不一致等情形
不能快取的請求
當然並不是所有請求都能被快取。
無法被瀏覽器快取的請求:
- HTTP資訊頭中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告訴瀏覽器不用快取的請求
- 需要根據Cookie,認證資訊等決定輸入內容的動態請求是不能被快取的
- 經過HTTPS安全加密的請求(有人也經過測試發現,ie其實在頭部加入Cache-Control:max-age資訊,firefox在頭部加入Cache-Control:Public之後,能夠對HTTPS的資源進行快取)
- POST請求無法被快取
- 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進行強制重新整理的時候,只是所有的快取機制都將失效,重新從伺服器拉去資源。
更多可以參考瀏覽器快取機制
總結
盜圖瀏覽器快取機制,兩張圖很清晰
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2015-08/121429p2.htm
相關文章