web快取機制

Jack_Chou發表於2018-04-09

使用快取的好處

快取廣泛存在於我們的日常上網過程中。使用快取主要有幾個好處:

一是能夠減輕伺服器端的訪問壓力;

二是使用本地快取能夠加快頁面的載入速度,提升使用者體驗。

web快取分類

在瞭解如何使用快取來優化我們的web應用之前,我們先來看看web快取具體有哪些分類:

瀏覽器快取

瀏覽器可以將一些比較消耗頻寬而且幾乎不會變更的靜態資源快取在本地,當使用者需要再次使用這部分資源(比如使用者在點選瀏覽器中的頁面前進或後退按鈕等)時,就可以直接從瀏覽器快取中載入,而不需要再向伺服器發起請求。

伺服器端快取

典型代表就是反向代理伺服器快取,反向代理伺服器大多數時候就是在分擔伺服器端的訪問壓力,能儘量減少向源伺服器傳送請求就儘量減少,能儘可能地利用快取下來的資源就儘可能地利用。一些大型站點會使用CDN,目的是為了讓使用者就近訪問,加快頁面的載入速度,而在每個CDN節點同樣也會快取使用者訪問過的資源,因此也屬於伺服器端快取的範疇。

快取手段

要實現瀏覽器和伺服器對使用者重複訪問的資源進行快取控制,主要有兩種途徑:

一種是通過設定 <meta> 標籤來控制:

<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
複製程式碼

注意上面兩條指令的作用是不允許瀏覽器快取當前頁面以及頁面上引用的資源,強制瀏覽器每次請求當前頁面時都需要從伺服器端獲取最新版本。這種方式僅對部分瀏覽器有效,而且不影響代理伺服器對該頁面的快取控制,原因是因為代理伺服器並不會去解析頁面上的內容。

另外一種應用更加廣泛的方法就是通過 http 協議的頭部欄位進行快取的控制。而通過 http 頭部欄位進行控制又可以從兩個角度來控制快取:分別是新鮮度(對應 Cache-ControlExpires)和校驗值(對應 Last-ModifiedEtag)。其實快取控制就是在關注這樣兩個問題:一方面是可以快取多長時間(新鮮度),另一方面則是如何判斷快取失效(校驗)。

Cache-Control/Expires

Cache-Control / Expires 這一組頭部欄位都可以用來指定快取的有效期,區別在於前者除了可以指定快取有效期(通過設定max-age引數,該引數值和Expires具體含義還不相同,前者指定快取時長,而後者指定快取過期的時間點)之外,還可以實現更加精細的快取控制功能;而後者只能單一地設定快取的有效期。

Cache-Control 是一個通用首部,並且在請求頭和響應頭使用的引數還略微有些差異。下面就具體介紹一下 Cache-Control 可以設定的引數都有哪些以及它們所代表的含義(此處僅介紹出現在伺服器端響應中的 Cache-Control 可能會用到的引數)。

  • max-age=<num>: 前面提到過了,這裡不再贅述。單位:秒
  • s-max-age=<num>: 作用和 max-age 類似,區別在於該引數僅對提供共享快取的公共代理伺服器起作用
  • no-store:不允許客戶端和快取伺服器對響應進行快取,意味著每次都會重新向源伺服器發起請求(注意不是條件請求)
  • no-cache:應用這個值時,客戶端和快取伺服器在使用快取資源之前需要向源伺服器發起條件請求確認,類似於max-age=0,注意此時瀏覽器和快取伺服器是可以快取響應的
  • public:此引數是對快取伺服器起作用的,表示允許向多個不同的使用者提供同一份快取(共享快取)
  • private:與public剛好相反,表示當前快取的內容僅能對當前使用者提供

Cache-Control 首部的值可以由多個引數組合而成,引數之間使用逗號隔開。

Expires 首部用於指定快取失效的時刻,時間是以伺服器上的時間為準。

Last-Modified/Etag

伺服器可以通過Last-Modified響應首部來指明當前訪問資源最近一次修改是在什麼時候,客戶端在傳送請求校驗資源是否發生修改時,可以通過If-Modified-Since請求首部向伺服器端確認資源在給定時間點之後是否發生更改,伺服器可以根據比對資源的最近一次修改時間和If-Modified-Since首部指定的時間來決定返回304狀態碼還是返回新的資源。

使用Last-Modified/If-Modified-Since比對檔案修改時間的這套機制存在的問題就是:

  1. 因為Last-Modified指定的修改時間是精確到秒級的,如果伺服器上的檔案在一秒內發生多次更改,單純依靠檔案修改時間就檢測不到檔案已經發生變化

  2. 如果檔案在某個時間段內發生多次更改,但是前後檔案內容並沒有發生變化,這時依靠檔案修改時間去判斷檔案已經發生修改也不合適

而通過Etag響應首部就能夠解決上面提到的問題。通過對當前檔案內容計算生成一個唯一標識,通過比對標識是否相同來判斷檔案內容是否發生變化。相比於比較檔案修改時間,這種方式更加有效。客戶端在傳送條件請求時,會包含 If-None-Match 請求首部,來判斷檔案內容是否發生變化。

web快取機制

Pragma

除了可以使用上面提到的這些首部來控制快取之外,另外還有一個古老的響應首部就是 Pragma。Pragma 首部的值只有一個可選值就是 no-cache ,含義和 Cache-Control: no-cache 是一樣的。但是 Pragma 優先順序要高於 Cache-Control,而Cache-Control則又比Expires首部具有更高的優先順序。

瀏覽器行為

除了可以通過 http 首部來控制快取之外,在瀏覽器中不同的操作行為同樣也會影響瀏覽器是否使用快取。

當使用者點選前進或後退按鈕,無論伺服器響應的Cache-Control是什麼值(no-store除外),瀏覽器都會直接使用硬碟中快取的內容。

Cache-Control/Expires指定的有效期內如果使用者輸入URL地址之後回車(包括點選頁面的重新整理按鈕或者 F5),此時瀏覽器會直接使用硬碟中的快取內容(如下圖),而不會向伺服器發起請求。而此時如果快取期限已過,而之前伺服器響應中包含 Last-Modified/Etag 首部(一般伺服器返回的響應都會包含這兩個首部,不需要額外的配置),則會向伺服器發起條件請求,如果伺服器端內容沒有發生更改,則會響應 304 狀態碼,提示瀏覽器快取有效。

web快取機制

當點選 Ctrl+F5 時,即使快取的資源依然有效(仍然在 Cache-Control 首部包含的 max-age 引數指定的期限之內),瀏覽器也會向伺服器發起請求(注意此時的請求就不是條件請求了,而是強制從伺服器獲取最新的內容)。

結論

文章中我們主要探討了如何通過 http 首部來控制瀏覽器端快取,主要可以概括為以下幾點:

  1. Cache-Control 可以精細地控制快取,但是需要注意的一點是,它是在 HTTP/1.1 中才出現的,如果需要相容舊版本,在指定快取時長時可以和 Expires 首部一起搭配使用,如果要指定為 no-cache 則可以和 Pragma 首部一起使用

  2. 不同的瀏覽器操作行為同樣會影響瀏覽器是否使用本地快取,有些行為(比如點選前進後退按鈕)會直接從瀏覽器快取中返回內容,有些行為(比如當快取過期時點選重新整理按鈕等)則會導致瀏覽器傳送條件請求確認資源是否更新

  3. 優先使用Etag/If-None-Match來校驗資源是否有效

相關文章