雲計算

看了這篇,關於瀏覽器緩存你還有哪些疑問?

【轉載請註明出處】:https://blog.csdn.net/huahao1989/article/details/107730210

整個 Web 系統架構在HTTP 協議之上, 利用 HTTP 的緩存機制不僅可以極大地減少服務器負載, 更重要的是加速頁面的載入,以及減少用戶的流量消耗。 快速到達和易於訪問是 Web 與生俱來的特性, 其緩存機制也早已被服務器和瀏覽器廠商廣泛地實現, 我們作為 Web 內容的作者何樂而不為呢?

HTTP 緩存簡介

談起 HTTP 緩存你首先想到的一定是磁盤緩存,以及 304 狀態碼。 這是瀏覽器處理緩存的兩種情況:

  • 瀏覽器詢問服務器緩存是否有效,服務器返回 304 指示瀏覽器使用緩存。
  • 資源仍然處於有效期時,瀏覽器會直接使用磁盤緩存(在刷新時稍有不同)。

image.png

每個狀態的詳細說明如下:

1、Cache-Control

Cache-Control 在 HTTP 響應頭中,用於指示代理和 UA 使用何種緩存策略。比如:

  • no-cache為本次響應不可直接用於後續請求(在沒有向服務器進行校驗的情況下)
  • no-store為禁止緩存(不得存儲到非易失性介質,如果有的話儘量移除,用於敏感信息)
  • private為僅 UA 可緩存
  • public為大家都可以緩存。

Cache-Control為可緩存時,同時可指定緩存時間(比如public, max-age:86400)。 這意味著在 1 天(60x60x24=86400)時間內,瀏覽器都可以直接使用該緩存。 當然瀏覽器也有權隨時丟棄任何一項緩存,因此這裡可能有一致性問題。

2、Etag

如果資源本身確實會隨時發生改動,還用 Cache-Control 就會使用戶看到的頁面得不到更新。 但如果還希望利用 HTTP 緩存,這就需要有條件的(conditional)HTTP 請求。

HTTP協議規格說明定義ETag為“被請求變量的實體標記”,弱實體只要內容語義沒變即可,強實體指字節必須完全一致,建議使用弱實體。

如果響應體包含Etag字段,則瀏覽器在下次發送請求時會帶 If-None-Match 頭字段, 來詢問服務器該版本是否仍然可用。如果服務器發現該版本仍然是最新的, 就可以返回 304 狀態碼指示 UA 繼續使用緩存。
類似服務器端返回的格式:
ETag: W/"3ae83efccfc543bad6866e325cd8bfb9"

客戶端的查詢更新格式是這樣的:
If-None-Match:W/"3ae83efccfc543bad6866e325cd8bfb9"

如果ETag沒改變,則返回狀態304。

3、Last-Modified

在瀏覽器第一次請求某一個URL時,服務器端的返回狀態會是200,內容是請求的資源,同時有一個Last-Modified的屬性標記(HttpReponse Header)此文件在服務期端最後被修改的時間,格式類似這樣:
Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT

客戶端第二次請求此URL時,根據HTTP協議的規定,瀏覽器會向服務器傳送If-Modified-Since報頭(HttpRequest Header),詢問該時間之後文件是否有被修改過:
If-Modified-Since:Tue, 24 Feb 2009 08:01:04 GMT

如果服務器端的資源沒有變化,則自動返回HTTP 304(NotChanged)狀態碼,內容為空,這樣就節省了傳輸數據量。當服務器端代碼發生改變或者重啟服務器時,則重新發出資源,返回和第一次請求時類似。從而保證不向客戶端重複發出資源,也保證當服務器有變化時,客戶端能夠得到最新的資源。

注:如果If-Modified-Since的時間比服務器當前時間(當前的請求時間request_time)還晚,會認為是個非法請求

4、Expires

給出的日期/時間後,被響應認為是過時。如Expires:Thu, 02 Apr 2009 05:14:08 GMT

需和Last-Modified結合使用。用於控制請求文件的有效時間,當請求數據在有效期內時客戶端瀏覽器從緩存請求數據而不是服務器端。當緩存中數據失效或過期,才決定從服務器更新數據。

5、Last-Modified和Expires

Last-Modified標識能夠節省一點帶寬,但是還是逃不掉髮一個HTTP請求出去,而且要和Expires一起用。而Expires標識卻使得瀏覽器乾脆連HTTP請求都不用發,比如當用戶F5或者點擊Refresh按鈕的時候就算對於有Expires的URI,一樣也會發一個HTTP請求出去,所以,Last-Modified還是要用的,而且要和Expires一起用。

6、Etag和Expires

如果服務器端同時設置了Etag和Expires時,Etag原理同樣,即與Last-Modified/Etag對應的HttpRequestHeader:If-Modified-Since和If-None-Match。我們可以看到這兩個Header的值和WebServer發出的Last-Modified, Etag值完全一樣;在完全匹配If-Modified-Since和If-None-Match即檢查完修改時間和Etag之後,服務器才能返回304.

7、Last-Modified和Etag

分佈式系統裡多臺機器間文件的last-modified必須保持一致,以免負載均衡到不同機器導致比對失敗

分佈式系統儘量關閉掉Etag(每臺機器生成的etag都會不一樣)

Last-Modified和ETags請求的http報頭一起使用,服務器首先產生Last-Modified/Etag標記,服務器可在稍後使用它來判斷頁面是否已經被修改,來決定文件是否繼續緩存

過程如下:

  1. 客戶端請求一個頁面(A)。
  2. 服務器返回頁面A,並在給A加上一個Last-Modified/ETag。
  3. 客戶端展現該頁面,並將頁面連同Last-Modified/ETag一起緩存。
  4. 客戶再次請求頁面A,並將上次請求時服務器返回的Last-Modified/ETag一起傳遞給服務器。
  5. 服務器檢查該Last-Modified或ETag,並判斷出該頁面自上次客戶端請求之後還未被修改,直接返回響應304和一個空的響應體。

注:

  1. Last-Modified和Etag頭都是由WebServer發出的HttpReponse Header,WebServer應該同時支持這兩種頭。
  2. WebServer發送完Last-Modified/Etag頭給客戶端後,客戶端會緩存這些頭;
  3. 客戶端再次發起相同頁面的請求時,將分別發送與Last-Modified/Etag對應的HttpRequestHeader:If-Modified-Since和If-None-Match。這兩個Header的值和WebServer發出的Last-Modified,Etag值完全一樣;
  4. 通過上述值到服務器端檢查,判斷文件是否繼續緩存;
8、關於 Cache-Control: max-age=秒 和 Expires

Expires = 時間,HTTP 1.0 版本,緩存的載止時間,允許客戶端在這個時間之前不去檢查(發請求)
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不會變化

9、瀏覽器刷新

正常重新加載

按下刷新按鈕或快捷鍵(在 MacOS 中是 Cmd+R)會觸發瀏覽器的“正常重新加載”(normal reload), 此時瀏覽器會執行一次 Conditional GET。 Cache-Control 等緩存頭字段會被忽略,並且帶If-None-Match, If-Modified-Since等頭字段。 此時服務器總會收到一次 HTTP GET 請求。 在 Chrome 中按下刷新,瀏覽器還會帶如下請求頭:
Cache-Control:max-age=0

注意:在地址欄重新輸入當前頁面地址並按下回車也會當做刷新處理, 這意味著只有從新標籤頁或超鏈接打開時,才能觀察到直接使用硬盤緩存的情況。

強制重新加載

在 Chrome 中按下 Cmd+Shift+R (MacOS)可以觸發強制重新加載(Hard Reload), 此時包括頁面本身在內的所有資源都不會使用緩存。 瀏覽器直接發送 HTTP 請求且不帶任何條件請求字段。 在 Chrome 中強制刷新,瀏覽器還會帶如下請求頭:
Cache-Control: no-cache
Pragma: no-cache

如何讓緩存的靜態文件失效

一般我們在頁面上引用很多js或者css文件,一旦請求過並且緩存在瀏覽器中的資源並沒有失效,這個時候發現我們有個bug需要修改或者有新的東西需要發佈,你要怎麼辦?有些人就說了,強制刷新下瀏覽器就好了,或者在請求的時候不返回304,直接返回新的資源內容,但是這樣並不好操作,一是用戶未必知道強制刷新或者清理緩存,二是我們只想在發佈新的內容之後第一次用戶的請求返回新的內容並緩存,後面還是走緩存;三是我們一般都會使用CDN,每次發佈完之後還需要清理CDN緩存,很是麻煩。其實有一個最簡單的辦法就是在引用這些靜態資源的時候加一個版本號即可,類似.../js/index.js?v=1.0這樣的,如果修改了內容,那麼只需要改一下版本號即可,瀏覽器自然會獲取到新的內容。

歡迎關注 “後端老鳥” 公眾號,接下來會發一系列的專題文章,包括Java、Python、Linux、SpringBoot、SpringCloud、Dubbo、算法、技術團隊的管理等,還有各種腦圖和學習資料,NFC技術、搜索技術、爬蟲技術、推薦技術、音視頻互動直播等,只要有時間我就會整理分享,敬請期待,現成的筆記、腦圖和學習資料如果大家有需求也可以公眾號留言提前獲取。由於本人在所有團隊中基本都處於攻堅和探路的角色,搞過的東西多,遇到的坑多,解決的問題也很多,歡迎大家加公眾號進群一起交流學習。

【轉載請註明出處】:https://blog.csdn.net/huahao1989/article/details/107730210

Leave a Reply

Your email address will not be published. Required fields are marked *