我與快取的相愛相殺

命名最頭痛發表於2019-04-18

快取是我們這些切圖仔繞不開的話題,但是你不理解它就會出現需要時,拿不到,不需要時,總是出來作怪,今天這篇文章將帶梳理下於快取有關的知識。

那麼什麼是快取呢?

快取是瀏覽器的一種機制,叫瀏覽器快取機制,該機制可以把一個請求過的 Web 資源(如 html 頁面,圖片,js,api資料等)拷貝一份到副本儲存在瀏覽器中,並根據請求配置選擇是否使用它們。

為什麼使用快取?

  • 減少網路頻寬消耗
  • 降低伺服器壓力
  • 減少網路延遲,加快頁面開啟速度

瀏覽器端快取規則:對於瀏覽器的快取來講,這些規則是在 HTTP 協議頭和 HTML 頁面的 Meta 標籤中定義的。

瀏覽器的快取控制

① 使用 HTML Meta標籤

我們可以在 HTML 頁面的節點中加入標籤

  <!- Pragma 是 http1.0 版本中給客戶端設定快取方式之一 ->
  <meta http-equiv="Pragma" content="no-cache">
複製程式碼

上述程式碼含義是:瀏覽器當前頁面不被快取,每次訪問都向伺服器請求,但是通過這種方式去禁用快取的形式用處很有限

  • 僅有 IE 才能識別這段 meta 標籤含義,其它主流瀏覽器僅識別 "Cache-Control: no-store" 的mete標籤
  • 在 IE 識別到該meta標籤含義,並不一定會在請求欄位加上 Pragma,但的確會讓當前頁面每次都發新請求(僅限頁面,頁面上的資源不受影響)

② 使用快取有關的 HTTP 訊息報頭,這個後面會詳細說

與快取有關的訊息報頭有expires,cache-control,pragma,Last-Modified,If-modified-since,Etag,If-none-match 等。

瀏覽器的快取機制可分為四個方面

  • Memory Cache
  • Service Worker Cache
  • HTTP Cache
  • Push Cache

這裡我們只研究 HTTP Cache

為什麼 HTTP 快取可以提升效率呢?

我們知道,請求是個複雜的過程,需要經過

DNS解析 -> 因特網的五層協議 -> 伺服器 -> 伺服器又要進行安全校驗 -> 根據請求執行相應的程式碼 -> 封裝成 HTTP 請求 -> 四次揮手

最後我們的瀏覽器才拿到資料,而快取就是把這一系列繁瑣的過程都省略掉,當下一個請求來到時,如果是相同的 URL,直接讀取相應的資料。

如果你還不理解,這裡舉個例子,商品的總部在北京,咦,它發現廣東這邊的市場對這個產品的需求量很大,那麼它就在廣州建一個倉庫,那麼廣東這邊購買該產品時,直接從廣州發貨,大大節省了時間,提升了效率.

好了,經過上面的解釋,我們理解了,快取是一種瀏覽器的機制,它會拷貝一份副本資料儲存在本地,當遇到相同的請求,直接從本地讀取資料,通過重複利用之前獲取資源的方式來減少IO消耗,從而提高了訪問速度,所以說,快取是效能優化的一種手段。

既然快取可以提高效率,那要怎麼用呢? 不可能毫無章法吧,沒錯,確實有一套快取規則,當輸入 url,會進行一次資料請求,返回 http 響應頭,就會攜帶相應的快取規則,因此,要快取規則可以由伺服器決定。

快取規則

HTTP快取有許多規則,根據是否需要重新向伺服器發起請求來分類,我將其分為兩大類(強快取和協商快取)

顧名思義,強快取意味著強制使用快取,協商快取意味著每用一次快取都要協商一次。 強快取和協商快取都允許使用情況下,優先強快取。

強快取

控制欄位:

  • Expires: HTTP1.0
  • Cache-Control: HTTP1.1 判斷過程:請求再次發起 -> 瀏覽器根據 expires 和 cache-control 判斷目標資源是否命中"強快取" -> 若命中,直接從快取獲取資源,不再與伺服器發生通訊。

Expires

響應頭中的expires

expires: Sat,30 Mar 2019 10:31:59 GMT

流程

  • 首次請求
  • 伺服器告知啟用強快取,並在響應頭中帶上 expires,告知快取到期時間,該值是個時間戳。
  • 隨後的每次請求,瀏覽器會先對比本地時間和 expires 的時間戳。
  • 如果本地時間小於 expires 設定的過期時間,那麼就直接去快取中取這個資源。

弊端: 依賴本地時間,如果使用者修改了本地時間,那麼 expires 就無法達到我們的預期。

基於對本地時間依賴這個弊端,HTTP1.1提出了 Cache-Control 來完全替代 expires 的任務,當兩者並存情況下,優先 Cache-Control,在當下的前端實踐裡,繼續使用 expires 的唯一目的就是向下相容

Cache-Control

響應頭中的 Cache-Control

cache-control: max-age=655350000

cache-control 中常用的值

max-age:指定的是從文件被訪問後的存活時間,這個時間是個相對值,相對的是文件第一次被請求時伺服器記錄的。

Request_time(請求時間),也就是,相對的是文件的請求時間(Atime),單位:s。

s-maxage: 表示向代理伺服器請求快取內容,只在代理伺服器中生效,優先順序高於 max-age,客戶端中只考慮 max-age。

pubilc 與 private: 針對資源是否能夠被代理伺服器快取而存在的一組對立概念,如果設定了 public,那麼它既可以被瀏覽器快取,也可以被代理伺服器快取,如果我們設定了 private,則只能被瀏覽器快取,預設值是 private。 no-cache: 每一次發起請求都不會再去詢問瀏覽器快取情況,而是直接向伺服器去確認資源是否過期(等於直接使用協商快取)。 no-store: 不使用任何快取策略,直接向服務端傳送請求,並下載完整的響應。

已存在快取資料時,僅基於強制快取,請求資料流程如下:

強快取命中 與未命中

強快取生效的網路請求圖

強快取生效流程圖
狀態碼為灰色的請求則代表使用了強制快取,請求對應的Size值則代表該快取存放的位置,分別為from memory cache 和 from disk cache。

協商快取

控制欄位: (HTTP1.0)

  • Last-Modified:是一個時間戳,由伺服器生成,如果規則中啟用了協商快取,它會在首次請求時,隨著 Response Headers 返回,存在響應頭中。
  • If-Modified-Since:也是一個時間戳,依託著 Last-Modified,是它的複製品,由瀏覽器生成,在啟用協商快取情況下,每次的請求頭中都會帶上這個欄位,它的值等於上一次響應頭返回的 last-modified 值。 (HTTP1.1)
  • Etag:伺服器為每個資源生成的唯一的標識字串,這個標識字串時基於檔案內容編碼的,只要檔案內容不同,它們對應的 Etag 就是不同的,由伺服器產生,存在響應頭中。
  • If-None-Match: Etag 的複製品,用法和 If-Modified-Since 一樣,存在請求頭中。

如果伺服器提示資源未改動(Not Modified),資源會被重定向到瀏覽器快取,這種情況下,網路請求對應的狀態碼是304。

304狀態碼請求圖

Last-Modified 與 If-Modified-Since

響應頭中的 Last-Modified

last-modified: Wed,19 Dec 2018 08:57:33 GMT 請求頭中的 If-Modified-Since If-Modified-Since: Mon,14 Jan 2019 03:31:14 GMT

流程

  • 首次請求
  • 伺服器告知啟用協商快取規則,並在響應頭中帶上 Last-Modified,告知快取到期時間
  • 隨後的每次請求,請求頭上都會攜帶 If-Modified-Since,該值等於上一次響應頭中的 Last-Modified 的值
  • 伺服器收到 If-Modified-Since 後,會將該屬性的值與伺服器上資源的最後修改時間進行匹配,從而判斷資源是否發生了變化
  • 如果發生變化會返回一個完整的響應內容,在響應頭中新增新的 Last-Modified 值,否則,只返回header部分,狀態碼為304,響應頭不會再新增 Last-Modified.

弊端: Last-Modified無法正確感知檔案的變化,譬如說,檔案的編輯時間修改了而內容沒有修改,或者修改檔案速度太快,幾毫秒就改一次檔案,If-Modified-Since 只能檢測秒級的變化.

為了解決這個問題,Etag 作為 Last-Modified 的升級版,因時而生

Etag 是通過標識字串來辨別檔案內容是否發生修改的,檔案內容不一致才會生成新的標識字串,這就彌補了 Last-Modified 時間戳的不足,通過 Etag 可以精準的感知檔案的變化.

Etag 與 If-None-Match

響應頭中的 Etag

etag: "2c6bee6a6ab9e39b892970e9368a3dff" 請求頭中的 If-None-Match If-None-Match: "2c6bee6a6ab9e39b892970e9368a3dff"

流程

  • 首次請求
  • 伺服器啟用協商快取情況下,會在響應頭中帶上 Etag
  • 隨後每次請求,請求頭上都會帶上 If-None-Match,該值等於上一次響應頭中的 Etag 的值
  • 伺服器收到 If-None-Match 後,會進行比對,從而判斷資源是否發生變化
  • 如果變化返回一個完整響應內容,在響應頭上新增新的 Etag 值,否則返回 304,響應頭不會在新增 Etag

弊端: Etag的生成需要伺服器付出額外的開銷,會影響服務端效能

Etag 並不能替代 Last-Modified,只能作為 Last-Modified 的補充和強化存在,當 Etag 和 Last-Modified 同時出現時,以 Etag 為準

已存在快取資料時,僅基於協商快取,請求資料的流程如下

協商快取命中 與未命中

協商快取生效的網路請求圖

協商快取生效流程圖

不能快取的請求

  • HTTP資訊頭中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告訴瀏覽器不用快取的請求
  • 需要根據Cookie,認證資訊等決定輸入內容的動態請求是不能被快取的
  • 經過HTTPS安全加密的請求
  • POST請求無法被快取
  • HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求無法被快取

相關文章