1. 程式人生 > >淺談HTTP快取

淺談HTTP快取

寫在前面:最近學習了修言同學的小冊,受益良多。對於HTTP快取這一塊,經過資料查詢和思考,也有了自己的一些思考認識,希望分享出來與大家一起討論和成長。

內容概述

  • 什麼是快取及快取的優點
  • 快取的處理步驟
  • 強快取和協商快取
  • 快取決策
  • 總結與思考

一、快取及其優點

快取

快取是一種可以自動儲存常見資源副本並可以在下一次請求中直接使用副本而非再次獲取的技術。

也就是說,當我們首次進行資源請求之後,伺服器在返回資源給客戶端的同時,快取伺服器或本地快取也會儲存一份資源副本(在允許快取的情況下),當我們下次再對該資源進行請求時,則會直接使用資源副本而不會從原始伺服器再次請求文件。

快取的優點

  1. 快取可以減少冗餘的資料傳輸。
  2. 快取可以緩解網路瓶頸的問題。
  3. 快取可以降低對原始伺服器的要求。
  4. 快取可以降低請求的距離時延。

資料的冗餘傳輸

當很多客戶端訪問同一份文件的時候,原始伺服器一遍又一遍地返回給不同的客戶端相同內容的文件,這些重複的文件造成了資料的冗餘傳輸。

網路瓶頸問題

在大部分情況下,客戶端訪問代理伺服器的速度總是比訪問原始伺服器更快(頻寬大、延遲低),因此如果代理伺服器能夠提供一份完整的副本,則遠遠比從原始伺服器獲取來的快且省流量——尤其針對大檔案來說。

降低原始伺服器要求

突發事件(比如爆炸性新聞、某個名人事件)使很多人幾乎同時去訪問一個Web文件時, 就會出現瞬間擁塞。由此造成的過多流量峰值可能會使網路和Web伺服器產生崩潰。使用快取便可在一定程度上降低對原伺服器的壓力。

降低請求距離時延

在物理上的距離,也是降低web效能的一個方面。對於同一份資源,原伺服器離請求端越近,資源的獲取速度則會越快。

二、強快取和協商快取

1、 快取相關概念解釋

快取命中

如果某個請求的結果是由已快取的副本提供的,被稱作快取命中。

快取未命中

如果快取中沒有可用的副本或者副本已經過期,則會將請求轉發至原始伺服器,這被稱作快取未命中 。

新鮮度檢測

HTTP通過快取將伺服器文件的副本保留一段時間。在這段時間裡, 都認為文件是“新鮮的”,快取可以在不聯絡伺服器的情況下,直接提供該文件。但一旦已快取副本停留的時間太長,超過了文件的新鮮度限值(freshness limit), 就認為物件“過時”了,在提供該文件之前,快取要再次與伺服器進行確認,以檢視文件是否發生了變化。

再驗證

原始伺服器上的內容可能會隨時變化,快取需要經常對其進行檢測,看看它儲存的副本是否仍是伺服器上最新的副本。這些新鮮度檢測被稱為 HTTP 再驗證。

快取可以隨時對副本進行再驗證,但大部分快取只在客戶端發起請求,並且副本舊得足以需要檢測的時候,才會對副本進行再驗證。

再驗證命中和再驗證未命中

快取對快取的副本進行再驗證時,會向原始伺服器傳送一個再驗證請求,如果內容沒有發生變化,伺服器會以304 Not Modified進行響應。這被稱作是再驗證命中或者緩慢命中。如果內容發生了變化,伺服器會以200進行響應。這被稱作再驗證未命中。

2、 快取的處理步驟

  • 首先是當用戶請求資源時,會判斷是否有快取,如果沒有,則會向原伺服器請求資源。
  • 如果有快取,則會進入強快取的範疇,判斷快取是否新鮮,如果快取新鮮,則會直接返回快取副本給客戶端。如果快取不新鮮了,則表示強快取失敗,將會進入到協商快取。
  • 協商快取將判斷是否存在Etag和Last-Modified首部,通過這些首部驗證資源是否發生過變化,如果未發生變化,則表示命中了協商快取,會重定向到快取副本,將資源返回給客戶端,否則的話表示協商快取未命中,伺服器會返回新的資源。

大家可以先看幾遍這張圖,在腦海裡對快取的過程有一個巨集觀的瞭解,接下來我會對這張圖上的各個部分進行解讀。等到最後,再回來看這張圖便會覺得快取是如此簡單的一個過程了。

3、 強快取和協商快取的概念

強快取

服務端告知客戶端快取時間後,由客戶端判斷並決定是否使用快取。

即首次發起請求時,服務端會在Response Headers 中寫入快取新鮮時間。當請求再次發出時,如果快取新鮮,將直接從快取獲取資源,而不會再與伺服器發生通訊。

協商快取

由服務端決定並告知客戶端是否使用快取。

協商快取機制下,瀏覽器需要向伺服器去詢問快取的相關資訊,進而判斷是重新發起請求、下載完整的響應,還是從本地獲取快取的資源。

4、 強快取和協商快取的實現原理

(1) 強快取實現原理

強快取是通過Expires首部或Cache-Control: max-age來實現的。

Expires 和 Cache-Control: max-age都是用來標識資源的過期時間的首部。

Expires(HTTP/1.0)

Expires描述的是一個絕對時間,由伺服器返回,用GMT格式的字串表示。

由於expires是一個絕對時間,如果人為的更改時間,會對快取的有效期造成影響,使快取有效期的設定失去意義。因此在http1.1中我們有了expires的完全替代首部cache-control:max-age

Cache-Control(HTTP/1.1)

max-age值是一個相對時間,它定義了文件的最大使用期——從第一次生成文件到文件不再新鮮、無法使用為止,最大的合法生存時間(以秒為單位)。

過程說明

  • 當我們首次請求資源時,伺服器在返回資源的同時,會在Response Headers中寫入expires首部或cache-control,標識快取的過期時間,快取副本會將該部分資訊儲存起來。
  • 當再次請求該資源的時候,快取會對date(Date 是一個通用首部,表示原始伺服器訊息發出的時間。即表示的是首次請求某個資源的時間。)和expires/cache-control的時間進行對比,從而判斷快取副本是否足夠新鮮。

(2) 協商快取實現原理

協商快取是通過請求頭Last-Modified或Etag來實現的。

Last-Modified 標識的是文件最後修改時間,Etag 則是以文件內容來進行編碼的。

Last-Modified

說明:

  • 首次請求資源時,伺服器在返回資源的同時,會在Response Headers中寫入Last-Modified首部,表示該資源在伺服器上的最後修改時間。
  • 當再次請求該資源時,會在Request Headers 中寫入If-Modified-Since首部,此時的If-Modified-Since的值是首次請求資源時所返回的Last-Modified的值。
  • 伺服器接收到請求後,會根據If-Modified-Since的值判斷資源在該日期之後是否發生過變化。
  • 如果沒有,則會返回304 Not Modified;如果變化了,則會返回變化過後的資源,同時更新Last-Modified的值。

(1)資源未更新network面板截圖

首次請求:

再次請求:

(2)資源發生更新network面板截圖

首次請求:

再次請求:(大家可以看到Last-Modified和If-Modified-Since標識的時間不一樣了)

Etag

我們可以看到,Etag的實現過程和Last-Modified完全一樣,具體過程可參照Last-Modified,在這裡就不做過多介紹了。

Last-Modified存在的一些問題

有些文件可能會被週期性地重寫,但實際包含的資料常常是一樣的。儘管內容沒有變化,但修改日期會發生變化。

有些文件可能被修改了,但所做修改並不重要,不需要讓快取過載資料(比如對拼寫或註釋的修改)。

有些伺服器提供的文件會在亞秒間隙發生變化(比如,實時監視器),對這些伺服器來說,以一秒為粒度的修改日期可能就不夠用了。

通過這些描述,我們可以總結出一些Last-Modified存在的缺陷:

  1. 無法感知檔案內容是否真的發生了變化。 不該重新請求的時候,也會重新請求。
  2. 在秒以下的內容變化無法感知到。 該重新請求的時候,反而沒有重新請求。

對於上述問題,Etag作為Last-Modified的補充而出現,Etag 是由伺服器為每個資源生成的唯一的標識字串,這個標識字串是基於檔案內容編碼的,只要檔案內容不同,它們對應的 Etag 就是不同的,因此 Etag 能夠精準地感知檔案的變化。

Etag 強驗證器和弱驗證器

ETag 分為強驗證器和弱驗證器。

強驗證器要求文件的每個位元組都相等,而弱驗證器只要求文件的含義相等。

強驗證:

弱驗證(前面會加上‘ W/’ 來標識):

5、 Cache-Control請求頭常用屬性說明

max-age/s-maxage

s-maxage指令的功能和max-age是相同的,它們唯一的不同點就在於s-maxage指令只適用於代理伺服器快取。s-maxage的優先順序高於max-age。

public/private

public 與 private 是針對資源是否能夠被代理服務快取而存在的一組對立概念。

如果我們為資源設定了 public,那麼它既可以被瀏覽器快取,也可以被代理伺服器快取;如果我們設定了 private,則該資源只能被瀏覽器快取。

no-cache/no-store

no-cache 表示客戶端要求快取在提供其已快取的副本之前必須先和原始伺服器對該文件進行驗證。即強制跳過強快取階段,直接進行協商快取。強快取並不能知道快取是否真的足夠新鮮,使用no-cache就是為了防止從快取中返回過期的資源,對快取進行再驗證。

no-store表示的是禁止快取,即每一次都是直接與原伺服器進行通訊,從原伺服器返回資源。一般設定了no-store的資源,都暗示著該資源具有敏感性資訊。

6、優先順序問題

(1)Expires 和 Cache-Control: max-age

應用HTTP/1.1版本的快取伺服器遇到同時存在Expires首部欄位的情況時,會優先處理max-age指令,而忽略掉Expires首部欄位。 而HTTP/1.0版本的快取伺服器的情況則相反,max-age指令會被忽略掉。

(2)Last-Modified 和 Etag(存疑部分)

看了很多資料都說Etag的優先順序高於Last-Modified,但是又有資料說當Etag和Last-Modified同時存在時,是由二者共同決定標識文件是否發生變化的。

因此我對這裡的優先順序做了這樣一番解讀:當二者同時存在時,瀏覽器會優先判斷Etag,如果If-None-Match和伺服器資源最後修改時間不一樣,則表示檔案發生過變化,則直接返回200,此時不需要再對If-Modified-Since做檢查。當Etag命中時,才會判斷Last-Modified是否也命中,只有當二者都命中的情況下,才從快取中獲取快取副本。

注意:此番觀點不知是否正確,但覺得這樣合乎情理,歡迎大家一起討論或是指點一二。

三、快取決策

由於並未有過http快取方面的實際應用經驗,在快取決策方面實在沒有什麼自己的見解。

四、思考和總結

學習關於HTTP快取方面的東西花費了不少時間,發現裡面的概念和知識點比較的雜亂,一開始無法將整個快取的過程串聯起來。通過對部落格和書籍的查閱,終於梳理清楚了快取的流程及各個步驟中涉及到的概念和知識點。既對整個流程有了清晰的把控,也能對流程的各個環節和細節有一定的瞭解。

思考了很久應該如何行文,才能夠既把大的流程講述清楚,又能夠兼顧每一步設計到的東西。 希望通過分享,能夠給大家帶來一些新的東西和思考,那我也就滿足了。

最後回憶一遍快取的過程,在腦海裡畫出這張圖:

最後感謝大家的閱讀,辛苦啦^_^

如有錯誤,歡迎指正 [email protected]

資料參考