web 快取
web 快取是可以通過自動儲存常見文件副本的 HTTP 裝置。當 Web 請求抵達快取時,如果本地有以快取的副本,就可以從本地儲存裝置而不是原始伺服器中提取這個文件。
1. 為什麼需要快取
- 冗餘的資料傳輸
有很多客戶端訪問一個流行的原始伺服器頁面時,伺服器會多次傳輸同一份文件,每次傳送給一個客戶端,一些相同的位元組會在網路中一遍遍的傳輸。這些冗餘的資料傳輸會耗盡昂貴的網路頻寬。而通過快取我們可以保留第一家伺服器響應的副本,後繼請求就可以由快取的副本來應對
- 頻寬瓶頸
快取還可以緩解網路的瓶頸問題。很多網路會為本地網路客戶端提供的頻寬比為遠端伺服器提供的頻寬要寬。客戶端會以路徑上最慢的網速訪問伺服器。如果客戶端從一個快速區域網的快取中得到一份副本,那麼快取就可以提高效能——尤其是要傳輸比較大的檔案時
- 瞬間擁塞
快取在破環瞬間擁塞時顯得非常中重要。突發事件(比如爆炸性新聞,批量 E-mail 公告, 或者某個名人事件)是很多人幾乎同時去訪問一個 web 文件時,就會出現瞬間擁塞。由此造成的過多流量峰值可能會使網路和 web 伺服器發生災難性的崩潰
- 距離時延
即使頻寬不是問題,距離也可能稱為問題。每臺網路路由器都會增加因特網流量的時延,即使客戶端和伺服器之間沒有太多路由器,光速本身也會造成時延長。將快取放在附近的機房裡可以將檔案傳輸距離從數千英里縮短為數十米
2. 快取的命中和未命中
但是快取無法儲存世界上每份文件的副本,這樣就會分成兩種情況:
- 可以用已有的副本為某些到達快取的請求提供服務,這被稱之為快取命中
- 其他一些到達快取的請求可能會由於沒有副本可用,而被轉發給原始伺服器,這被稱之為快取未命中
3. 新鮮度檢測規則
HTTP通過快取將伺服器文件的副本保留一段時間。在這段時間裡,都認為文件時新鮮的,快取可以在不聯絡伺服器的情況下,直接提供該文件。我們稱之為強快取命中,此時瀏覽器會返回200狀態碼(from cache)
但一旦以快取副本停留的時間太長,超過了文件的新鮮度限值,就認為文件過期了。
再提供文件之前,快取要再次與伺服器進行再驗證,已檢視文件是否發生了變化。我們稱之為協商快取
-
在驗證命中: 如果伺服器物件沒有被修改,伺服器會向客戶端傳送一個小的HTTP 304 Not Modeified響應
-
再驗證未命中: 如果伺服器物件與以快取副本不同,伺服器向客戶端送一條普通的帶有完整內容的HTTP 200 ok 響應
-
物件被刪除: 如果伺服器物件已經被刪除了,伺服器就回送一個404 Not Found 響應,快取也會將其副本刪除
4. 強快取原理
通過特殊的HTTPCache-Control
首部和Expries
首部,HTTP讓原始伺服器向每個文件附加了一個過期日期,這些首部說明了在多長時間內可以將這些內容視為新鮮的。
瀏覽器第二次傳送請求相同資源時,拿出過期時間和當前時間進行比較,如果在過期日期之前,則強快取命中,如果快取文件過期,快取就必須與伺服器進行核對,詢問文件是否過期,如果被修改過,就要獲取一份新鮮(帶有新的過期日期)的副本
4.1 強快取首部
Cache-Control: max-age
:
max-age
值定義了文件的最大使用期——從第一次生成文件到文件不再新鮮,無法使用為止,最大的合法生存時間(以秒為單位)
Expires
:
指定一個絕對的過期日期,如果過期日期已經過了,就說明文件不在新鮮了,不過由於我們可以去更改客戶端的時間,因此可以更改快取命中的結果。因此我們優先使用Cache-Control
Cache-Control
指令:
no-cache
和no-store
:
no-cache
表示必須先與伺服器確認返回的響應是否發生了變化,然後才能使用響應來滿足後續對同意網址的請求。因此如果存在合適的驗證令牌(ETag
),no-cache
會發起往返通訊來驗證快取的響應,但如果資源未發生變化,則可避免下載
no-store
表示直接禁止瀏覽器以及所有中間快取儲存任何版本的返回響應,例如,包含個人隱私資料或銀行業務資料的響應。每次使用者請求該資產時,都會向伺服器傳送請求,並下載完整的響應
public
與private
:
public
出現再響應首部,則即使他有關聯的HTTP驗證,甚至響應狀態程式碼程式碼通常無法快取,也可以快取響應。大多數情況下,public
不是必須的,因為明確的快取資訊(例如max-age
)已表示響應是可以快取
相比之下,瀏覽器可以快取private
響應。不過這些響應通常只為單個使用者快取,因此不允許任何中間快取對其進行快取,例如,使用者的瀏覽器可以快取包含使用者私人資訊的HTML網頁,但CDN不能快取
max-age
:
指令指定從請求的時間開始,允許獲取的響應被重用的最長時間。例如max-age=60
表示可以在接下來的60s快取和重用響應
must-revalidate
:
must-revalidate
告訴快取,再事先沒有跟原始伺服器進行再驗證的情況下,不能提供這個物件的陳舊副本,快取仍然可以隨意提供新鮮的副本。如果在快取進行must-revalidate
新鮮度檢查時,原始伺服器不可用,快取就必須返回一條504錯誤
最佳Cache-Control
策略:
5. 協商快取原理
僅僅是以快取過期了並不意味著他和原始伺服器目前處於活躍狀態的文件有實際的區別,這只是意味著到了要進行核對的時間了,這種情況被稱為協商快取,說明快取需要詢問原始伺服器是否發生變化
-
如果再驗證顯示內容發生了變化,快取會獲取一份新的文件副本,並將其儲存在舊文件的位置上,然後將文件傳送給客戶端。
-
如果再驗證內容沒有發生變化,快取只需要獲取新的首部,包括一個新的過期日期,並對快取中的首部進行更新,並對快取中的首部進行更新就行了
5.1 用條件方法進行再驗證
HTTP的條件方法可以高效的實現再驗證。HTTP允許快取向原始伺服器傳送一個條件GET,請求伺服器只有在文件與快取中現有的副本不同時,才回送物件主體,對於快取在驗證來說最有用的2個首部時
If-Modified-Since: <date>
:
如果從指定日期之後,文件被修改了,就執行請求的方法。可以與Last-Modfied
伺服器響應首部配合使用,只有在內容修改後與已快取版本有所不同的時候才去獲取內容
If-None-Match:<tags>
:
伺服器可以為文件提供特殊的標籤(ETag
),而不是將其與最近修改日期向匹配,這些標籤就像序列號一樣。如果已快取標籤與伺服器文件中的標籤有所不同,If-None-Match
首部就會執行所請求的方法
5.2 If-Modified-Since: / Last-Modified
具體流程如下:
-
客戶端第一次向伺服器發起請求,伺服器將最後的修改日期(
Last-Modified
)附加到所提供的文件上去 -
當再一次請求資源時間,如果沒有命中強快取,在執行在驗證時,會包含一個
If-Modifed-Since
首部,其中攜帶有最後修改已快取副本的日期:If-Modified-Since: <cached last-modified data>
-
如果內容被修改了,伺服器回送新的文件,返回200狀態碼和最新的修改日期
-
如果內容沒有被修改,會返回一個
304 Not Modified
響應
5.3 If-None-Match / ETag
有些情況下僅使用最後修改日期進行再驗證是不夠的
-
有些文件有可能會被週期性的重寫(比如: 從一個後臺程式中寫入),但實際上包含的資料常常是一樣分,儘管內容沒有變化,但修改日期會發生變化
-
有些文件可能被修改了,但所做修改並不重要.不需要讓世界範圍內的快取都重灌資料(比如填寫註釋)
-
有些伺服器無法準確判定其頁面的最後修改日期
-
有些伺服器提供的文件會在毫秒間隙發生變化(比如,實時監視器),對這些伺服器來說,以一秒為粒度的修改日期可能就不夠用了
因此HTTP允許使用者對被稱為實體標籤的(ETag
)的版本識別符號進行比較。實體標籤是附加到文件上的任意標籤(引用字串),伺服器生成並返回的隨機令牌通常是檔案內容的雜湊值或其他指紋。客戶端不需要指紋是如何生成的,只需在下一次請求時將其傳送至伺服器。如果指紋仍然相同,則表示資源未發生變化,您就可以跳過下載。
在上例中,客戶端自動在“If-None-Match” HTTP 請求標頭內提供 ETag 令牌。伺服器根據當前資源核對令牌。如果它未發生變化,伺服器將返回304 Not Modified響應,告知瀏覽器快取中的響應未發生變化,可以再延用 120 秒。請注意,您不必再次下載響應,這節約了時間和頻寬。
更新和廢棄響應
瀏覽器發出的所有HTTP請求會首先路由到瀏覽器快取,已確認是否快取可用於請求的有效響應。如果有匹配的響應,則從快取中讀取響應,這樣就避免了網路延遲和傳送產生的流量費用
不過如果我們向更新或廢棄快取的響應,該怎麼辦, 例如我們有一個css樣式表快取長達24小時,但是我們需要立即更新他,我們如何通知已過時的CSS快取副本的所有訪問者更新其快取。在不更改資源網址的情況下,是做不到的。
所以,如何才能實現客戶端快取和快速更新,你可以在資源內容發生變化時,更改它的網址,強制使用者下載新響應。通常情況下,可以通過再檔名中嵌入檔案的指紋或版本號來實現
-
HTML被標記為
no-cache
,這意味著瀏覽器再每次請求時都始終重新驗證文件,並在內容變化時獲取最新版本。此外再HTML標記內,再CSS和javascript中嵌入指紋,如果這些檔案的內容發生變化,網頁的HTML也會隨之改變,並會下載HTML響應的新副本 -
允許瀏覽器和中間快取(例如CDN)快取CSS,並將CSS設定為1年後到期,因為再檔名中嵌入了檔案的指紋,CSS更新時網址也會隨之變化
-
JavaScript同樣設定為1年後到期,但標記為
private
,這或許是因為它包含的某些使用者私人資料是CDN不應快取的。 -
影像快取時不包含版本或唯一指紋,並設定為一天後到期