淺談HTTP緩存(Web緩存)

本文主要介紹緩存的各類類型及在咱們工做中的使用技巧。css

什麼是Web緩存

先簡單經過幾個小例子,來簡單描述一下。html

場景1
測試妹子或者是qa小哥哥測功能時會說爲何個人瀏覽器的顯示亂七八糟,個人界面怎麼跟別人瀏覽器上不一致?旁邊的人會提醒說:清下緩存試試。

場景2
開發改了代碼,上了環境,發現不生效,這時候首先就是清緩存,清了瀏覽器緩存發現仍是不行,再檢查,發現是反向代理緩存。前端

那麼當咱們說web緩存的時候,咱們說的是什麼?什麼地方能夠緩存,何時用什麼類型的緩存。若是緩存使用不當的話,又會帶來什麼樣的問題,咱們又該如何去避免? node

其實,緩存就是把數據或者是咱們須要取到的內容,放到能更快訪問的地方。緩存對於先後端開發者來講,咱們使用緩存都是爲了可以更好地提高性能。
其中Web緩存就是爲了提高Web頁面訪問的性能,把能緩存的頁面或者數據緩存到可以更快取到的地方。nginx

Web緩存的類型


當一個瀏覽器發起請求時,會通過上圖幾個步驟,那麼緩存的地方其實就是上圖中的瀏覽器、反向代理、cdn、數據庫等等。
因此Web 緩存大體能夠分爲:數據庫緩存、服務器端緩存(代理服務器緩存、CDN 緩存)、瀏覽器緩存。web

數據庫緩存

數據庫緩存是指,當web應用的關係比較複雜,數據庫中的表不少的時候,若是頻繁進行數據庫查詢,很容易致使數據庫不堪重荷,網站顯示延遲等問題。爲了提供查詢的性能,將查詢後的數據放到內存中進行緩存,下次查詢時,能夠直接從內存緩存返回,提升響應效率,緩存數據庫壓力。
經常使用的數據庫緩存好比配合使用memcache、redis這種高性能的分佈式內存緩存服務器。redis

若是使用緩存的話,須要注意 緩存數據和真實數據源數據 的一致性。

服務器緩存

服務器緩存主要分爲代理服務器緩存和CDN緩存。數據庫

代理服務器緩存

代理服務器是瀏覽器和源服務器之間的中間服務器,瀏覽器先向這個中間服務器發起Web請求,通過處理後(好比權限驗證,緩存匹配等),再將請求轉發到源服務器。代理服務器緩存的運做原理跟瀏覽器的運做原理差很少,只是規模更大。後端

CDN緩存

CDN緩存通常是由網站管理員本身部署,爲了讓他們的網站更容易擴展並得到更好的性能。一般狀況下,瀏覽器先向CDN網關發起Web請求,網關服務器後面對應着一臺或多臺負載均衡源服務器,會根據它們的負載請求,動態將請求轉發到合適的源服務器上。從瀏覽器角度來看,整個CDN就是一個源服務器,從這個層面來講,瀏覽器和服務器之間的緩存機制,在這種架構下一樣適用。瀏覽器

瀏覽器緩存

瀏覽器緩存是全部web應用中都會使用的,每一個瀏覽器都實現了 HTTP 緩存,咱們經過瀏覽器使用HTTP協議與服務器交互的時候,瀏覽器就會根據一套與服務器約定的規則進行緩存工做。咱們能夠經過瀏覽器提供的開發者工具來查看。
瀏覽器緩存的類型不少,HTTP 緩存、indexDB、cookie、localstorage 等等。
以Chrome瀏覽器爲例,F12打開瀏覽器開發者工具,再選擇「Application」,能夠看到全部的緩存類型,以下圖所示,咱們能夠看到有HTTP文件緩存(Frames下),Local Storage,Session Storage,indexDB,Web SQL,Cookie,CacheStorage,Application Cache等等。

瀏覽器開發者工具

瀏覽器請求流程


從這張流程圖能夠看出,瀏覽器在發送文件請求時,能夠根據協議頭判斷從服務器端請求文件仍是從本地緩存讀取文件,影響瀏覽器的文件緩存主要有幾個屬性:expires、Etag、Last-Modified,這三個屬性是由http協議定義的。

Expires

image.png
用於設置靜態資源的過時時間。它的值一個GMT格式的時間字符串,好比expires:Fri, 27 Jul 2029 13:38:54 GMT。這個時間表明着這個資源的失效時間,在此時間以前,即命中緩存。這種方式有一個明顯的缺點,因爲失效時間是一個絕對時間,因此當服務器與客戶端時間誤差較大時,就會致使緩存混亂。因而發展出了Cache-Control。

Cache-Control

image.png
Cache-Control是一個相對時間,例如Cache-Control:3600,表明着資源的有效期是3600秒。因爲是相對時間,而且都是與客戶端時間比較,因此服務器與客戶端時間誤差也不會致使問題。能夠用於控制是否緩存、緩存的讀取權限、資源的有效期。只不過Cache-Control的選擇更多,設置更細緻,若是同時設置的話,其優先級高於Expires。
Cache-Control能夠由多個字段組合而成。下面簡單挑幾個來講明:

  • public 指示響應數據能夠被任何客戶端緩存
  • private 指示響應數據能夠被非共享緩存所緩存。這代表響應的數據只能被單個用戶(多是操做系統用戶、瀏覽器用戶)緩存,不能被代理服務器緩存。
  • no-cache 指示響應數據不能被任何接受響應的客戶端所緩存,強制從服務器進行拉取。可用在請求頭和響應頭中
  • no-store 指示所傳送的響應數據除了不能被緩存,也不能存入磁盤。通常用於敏感數據,以避免數據被複制。
  • must-revalidate 指示全部的緩存都必須從新驗證,在這個過程當中,瀏覽器會發送一個If-Modified-Since頭。若是服務器程序驗證得出當前的響應數據爲最新的數據,那麼服務器應當返回一個304 Not Modified響應給客戶端,不然響應數據將再次被髮送到客戶端。
  • proxy-revalidate 與must-revalidate類似,不一樣的是用來指示共享緩存。
  • max-age:(單位秒) 數據通過max-age設置的秒數後就會失效,至關於HTTP/1.0中的Expires頭。若是在一次響應中同時設置了max-age和Expires,那麼max-age將具備較高的優先級。(注:ngnix設置expires會被轉換爲max-age)

Last-Modified/If-Modified-Since

image.png
瀏覽器第一次請求一個資源的時候,服務器返回的header中會加上Last-Modify,Last-modify是一個時間標識該資源的最後修改時間,例如last-modified:Fri, 20 Dec 2019 03:34:57 GMT

當瀏覽器再次請求該資源時,當資源過時(使用Cache-Control標識的max-age),發現資源具備Last-Modified聲明,則再次向web服務器請求時帶上頭 If-Modified-Since,表示請求時間。web服務器收到請求後發現有頭If-Modified-Since 則與被請求資源的最後修改時間進行比對。若最後修改時間較新,說明資源又被改動過,則響應整片資源內容(寫在響應消息包體內),HTTP 200;若最後修改時間較舊,說明資源無新修改,則響應HTTP 304 (無需包體,節省瀏覽),告知瀏覽器繼續使用所保存的cache。

Etag/If-None-Match

與Last-Modify/If-Modify-Since不一樣的是,Etag/If-None-Match返回的是一個校驗碼(ETag: entity tag)。ETag能夠保證每個資源是惟一的,資源變化都會致使ETag變化。ETag值的變動則說明資源狀態已經被修改。服務器根據瀏覽器上發送的If-None-Match值來判斷是否命中緩存。

Etag:web服務器響應請求時,告訴瀏覽器當前資源在服務器的惟一標識(生成規則由服務器定義)。nginx中,etag會默認增長,若是須要關閉,須要在配置文件中設置:etag off;

If-None-Match:當資源過時時(使用Cache-Control標識的max-age),發現資源具備Etage聲明,則再次向web服務器請求時帶上頭If-None-Match (Etag的值)。web服務器收到請求後發現有頭If-None-Match 則與被請求資源的相應校驗串進行比對,決定返回200或304。

ETag 擴展說明

咱們對ETag寄予厚望,但願它對於每個url生成惟一的值,資源變化時ETag也發生變化。神祕的Etag是如何生成的呢?以Apache爲例,ETag生成靠如下幾種因子

  1. 文件的i-node編號,此i-node非彼iNode。是Linux/Unix用來識別文件的編號。是的,識別文件用的不是文件名。使用命令’ls –I’能夠看到。
  2. 文件最後修改時間
  3. 文件大小

生成Etag的時候,可使用其中一種或幾種因子,使用抗碰撞散列函數來生成。因此,理論上ETag也是會重複的,只是機率小到能夠忽略。

既生Last-Modified何生Etag?

你可能會以爲使用Last-Modified已經足以讓瀏覽器知道本地的緩存副本是否足夠新,爲何還須要Etag(實體標識)呢?HTTP1.1中Etag的出現主要是爲了解決幾個Last-Modified比較難解決的問題:

  1. Last-Modified標註的最後修改只能精確到秒級,若是某些文件在1秒鐘之內,被修改屢次的話,它將不能準確標註文件的修改時間
  2. 若是某些文件會被按期生成,當有時內容並無任何變化,但Last-Modified卻改變了,致使文件無法使用緩存

3.有可能存在服務器沒有準確獲取文件修改時間,或者與代理服務器時間不一致等情形

Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的惟一標識符,可以更加準確的控制緩存。Last-Modified與ETag是能夠一塊兒使用的,服務器會優先驗證ETag,一致的狀況下,纔會繼續比對Last-Modified,最後才決定是否返回304。

用戶行爲與緩存

瀏覽器的緩存除了和相關的http協議規則有關外,還與用戶行爲有關。

用戶操做 Expires/Cache-Control Last-Modified/Etag
地址欄回車 有效 有效
頁面連接跳轉 有效 有效
新開窗口 有效 有效
前進、後退 有效 有效
F5刷新 無效 有效
Ctrl+F5刷新 無效 無效

如何控制緩存

設置緩存的兩種方式:
一、web服務器配置
以ngnix爲例,在nginx.conf中設置:

location~ .*\.(gif|jpg|png|htm|html|css|js|flv|ico|swf)(.*){
    expires 1d;
}

上述配置表示這些靜態文件1天后過時。
若是想配置爲徹底不緩存,那麼能夠設置爲expires -1;(後面的數字配置爲負數),返回的header會被設置爲Cache-Control:no-cache。
2.後臺代碼寫入
例如:

response.setHeader("Cache-Control", "no-cache");  

3.html 的meta標籤

<meta http-equiv="Cache-Control" content="max-age=7200"/>

實際問題分析

1.引入緩存以後,主要有兩個問題:
(1)瀏覽器不知道有資源更新,仍是使用緩存中的老文件。
(2)各個文件緩存策略不一致,有關聯關係的文件,有的從服務器加載,有的直接取瀏覽器緩存的,這樣有可能會致使界面混亂。

2.解決方式
(1)Etag或Last-modified
Etag是服務端根據文件信息生成的字符串,當服務端文件更新時,Etag也會變化,這樣能保證當服務端文件更新時,取到新的文件內容。
可是Etag這種解決方式的問題是,請求仍是會發到服務端,由服務端進行判斷。
Last-modified與Etag相似。

(2)文件名後綴構建過程當中,把構建生成的文件加上隨機後綴,主入口html中的引用文件在構建中替換爲增長了文件名後綴的;主入口文件配置爲不緩存。當服務端更新文件時,因爲文件名後綴更改,瀏覽器緩存匹配不上,會直接到服務端獲取,服務端沒有更新文件時,在瀏覽器緩存獲取。這種方式效果較好,可是須要引入構建,對於已經使用了前端構建的web應用比較適用。

相關文章
相關標籤/搜索