HTTP 協議的緩存機制概述

HTTP 協議的緩存機制涉及到多個請求頭字段,並且整個緩存機制的細節行爲也存在各類狀況的差別,譬如說何時訪問本地緩存不發送請求,何時發送請求查看資源是否更新,獲取 response 什麼狀況下更新緩存等。之前我對此只知其一;不知其二隻是籠統的知道一些概念,譬如 Cache-Control 能夠控制緩存的時間和是否須要緩存,可是緩存過時後的行爲,有緩存後瀏覽器是否有 http 請求都不甚瞭解。因此特意 google 下,此篇是對此的知識梳理。php

協議概述

什麼狀況下可使用本地緩存?譬如說咱們用 get 方式請求了一個資源 http://mytest.domain.com/static/images/bg.png,那麼咱們下次再請求這個圖片資源的時候符合哪些條件可使用本地緩存呢?html

  • url 必須是 http://mytest.domain.com/static/images/bg.png,若是是 http://mytest.domain.com/static/images/bg.png?t=12312321 就會發起新的請求,由於 url 不一樣。
  • 發送請求的 method 必須可被緩存,譬如 get。
  • 第一次請求 response(即本地緩存)若是有一個 Vary 頭,他的值列出的是一系列 http header,第二次請求的請求頭中那些在 Vary 值中所列的頭,必須和第一次請求相同(具體規則)。
  • 第二次請求不包含請求頭 Pragma: no-cache
  • 第二次請求不包含請求頭 Cache-Control: no-cache|max-age=0
  • 第一次請求的 response(即本地緩存)不包含 Cache-Control: no-cache
  • 本地緩存沒有過時
  • 或者雖然本地緩存已通過期,可是服務器驗證緩存和服務器資源一致,容許使用本地緩存的狀況(即得到304 response)

注1:上述任何提一個條件均可以被 cache-control extension 覆蓋算法

注2:response header中不只僅能夠 Cache-Control: no-cache,還能夠 Cache-Control: no-cache="Set-Cookie" 詳見瀏覽器

如何計算本地緩存是否過時

瀏覽器是經過比較緩存剩餘有效時間和當前緩存已存在時間來判斷的:response_is_fresh = (freshness_lifetime > current_age)freshness_lifetime 取值優先級次序以下列表所示(排在上面的優先級越高):緩存

  1. Cache-Control: s-maxage=xx
  2. Cache-Control: max-age=xx
  3. Expires: xxxxx
  4. 按規則進行計算(推測)

注1;若是有多個重複的上述頭,那麼是非法的,視做 response(資源)過時服務器

freshness_lifetime 計算(推測)規則:

規範並無給出具體的算法,可是給出了最壞狀況(but does impose worst-case constraints on their results),若是 response(資源)有 Last-Modified 頭,那麼推薦用從當前到lastmodified這個時間段的 10% 做爲 freshness_lifetime,而且 response(資源)的 current_age 若是已經超過24小時,必須在這個 response 上加上113 warn-code頭。不多有瀏覽器實現了freshness_lifetime的自助計算(推測),因此仍是仍是鼓勵給出上述顯式的1 - 3三種狀況。網絡

current_age 計算規則:

泛泛來講就是 response(資源)在本地的駐足時間加上網絡傳輸時間,由於網絡傳輸時間的計算有多個條件,規範實在看的我頭暈,因此具體計算規則詳見規範dom

本地緩存過時,如何經過服務器驗證緩存的是否依然有效(即304的狀況)

首先,若是第一次的 response(資源)頭中顯示申明瞭一些禁止緩存的頭(譬如:"no-store" or "no-cache" 等等),就不存在過時不過時的問題,由於這個資源不容許緩存。其次,過時緩存也不必定不可用,若是在斷網或者第二次請求帶上 max-stale 這個請求頭,那麼瀏覽器可使用過時的緩存(masx-stale 表示在緩存過時後多少時間內瀏覽器依然可使用緩存)。碰到過時緩存,瀏覽器能夠發送一個條件請求(conditional request)。這個請求的 url 依然是第一次請求的 url,只是會帶上些當前資源的一些信息,以供服務器驗證這個緩存是否依然可用仍是須要更新:google

  1. response(資源)的 Last-Modified 頭所帶的值會放到條件請求的 If-Modified-Since 頭中,或者是 If-Unmodified-Since 又或者 If-Range。
  2. response(資源)的 ETag 頭所帶的值會放到條件請求的 If-None-Match 頭中,或者 If-Match 又或者 If-Range。

服務器會根據不一樣的條件請求頭來驗證資源,具體的行爲詳見規範,這裏不細緻展開。針對條件請求,服務器返回會有三種狀況:url

  1. 一個帶有304 status code 的返回。表示緩存能夠被更新和重用。
  2. 一個帶有 body 的完整的 response。表示用這個 response 做爲請求的返回,而且視條件能夠用這個完整的 response 替換瀏覽器原有的緩存(此資源)。
  3. 若是返回一個5xx的 response,那麼瀏覽器能夠選擇就顯示這個5xx的返回,或者使用本地緩存(儘管多是過時的)- 規範沒有規定應該選擇哪一種處理,應該是取決於瀏覽器的行爲。

若是是一個304的返回,規範說這個返回能夠更新本地緩存,更新策略分三種:

  1. 若是這個304 response 帶有資源有效性的強驗證頭,那麼瀏覽器會尋找本地緩存,尋找那些帶有一樣強驗證頭的緩存,而後用這個最新的 response 去更新這些匹配的緩存(同一個資源可能瀏覽器保存有多份緩存,譬如日期不一樣等)。
  2. 若是這個304 response 帶有資源有效性的弱驗證頭,那麼瀏覽器一樣會找相匹配的緩存,可是隻會更新最新的那條匹配的緩存。
  3. 若是這個304 response 沒有帶有任何資源有效性驗證頭,而且瀏覽器緩存只有一份,而且這份也一樣沒帶有任何資源有效性驗證頭,那麼瀏覽器就會用這個304 response,更新本地緩存。

協議流程圖

假設第一次請求一個資源,返回 header 裏面帶上以下字段:
Cache-Control: max-age=600
Last-Modified: Wed, 28 Aug 2013 10:36:42 GMT
ETag: "124752e0d85461a16e76fbdef2e84fb9"

拋開細枝末節的東西,那麼第二次請求一般大體流程圖以下:

當前資源緩存是否過時:response_is_fresh = (freshness_lifetime > current_age)
                                            |
                               -----------------------------------
                              |                                   |        
                              是                                  否
                              |                                   |
                  發送請求,帶上請求頭                          從本地緩存中獲取資源(不發請求)
                  If-Modified-Since: 此資源Last-Modified的值      
                  If-None-Match: 此資源ETag的值
                              |
                  服務器根據 If-Modified-Since 和 If-None-Match
                  兩個值判斷資源是否更新過
                              |
                  -------------------------
                 |                         |
                 是                        否
                 |                         |
返回一個 status code:200 的 response    返回一個 status code:304 的 response
response body 裏面是請求的資源           response body 爲空
                 |                         |
瀏覽器用 response body裏面的資源         依然從本地緩存裏面獲取資源
替換本地緩存中的資源

特殊狀況

當你去瀏覽器驗證的時候可能會碰到一些特殊狀況,就是緩存有效,可是你刷新瀏覽器依然發送的條件請求。實際上是由於瀏覽器在請求頭中加入了一些料,譬如: Cache-Control: max-age=0。你刷新的方式能夠有不少種,譬如:按F5,按ctrl+F5,在地址欄按回車等等。這些不一樣的行爲都會影響瀏覽器發送請求的行爲。這裏有一些參考《在瀏覽器地址欄按回車、F五、Ctrl+F5刷新網頁的區別

參考資料

rfc7234

相關文章
相關標籤/搜索