一文搞定「快取」

2020-10-28 18:01:13

欄目介紹搞定快取。

前言

快取是指:為了降低伺服器端的存取頻率,減少通訊數量,前端將獲取的資料資訊儲存下來,當再次需要時,就使用所儲存的資料。

快取對使用者體驗和通訊成本都會造成很大的影響,所以要儘可能地去靈活使用快取機制。

快取的工作原理

HTTP快取是一個以時間為維度的快取。

瀏覽器在第一次請求中快取了響應,而後續的請求可以從快取提取第一次請求的響應。從而達到:減少時延而且還能降低頻寬消耗,因為可能壓根就沒有發出請求,所以網路的吞吐量也下降了。

工作原理

瀏覽器發出第一次請求,伺服器返回響應。如果得到響應中有資訊告訴瀏覽器可以快取此響應。那麼瀏覽器就把這個響應快取到瀏覽器快取中。

如果後續再發出請求時,瀏覽器會先判斷快取是否過期。如果沒有過期,瀏覽器壓根就不會向伺服器發出請求,而是直接從快取中提取結果。

比如:存取掘金站點

Size中可以看出,disk cache是從硬碟中提取的快取資訊。

快取過期了

如果快取過期了,也並不一定向第一個請求那樣伺服器直接返回響應。

瀏覽器的快取時間過過期了,就把該請求帶上快取的標籤傳送給伺服器。這時如果伺服器覺得這份快取還能用,那就返回304響應碼。瀏覽器將繼續使用這份快取。

比如:選擇上面圖中的其中一份快取檔案,copy請求urlcurl中展示
首先加-I獲取原始請求,檢視etaglast-modified頭部。

因為瀏覽器快取過期之後,請求就會帶上這些頭部一起傳送給伺服器,讓伺服器判斷是否還能用。
針對etag頭部,加一個if-none-match頭部帶上etag的值詢問伺服器。當然也可以針對last-modified頭部,加一個if-modified-since頭部詢問。

返回的是304。304的好處就是不攜帶包體,也就是說content-length為0,這樣就節省了大量的頻寬。

共用快取

瀏覽器快取是私有快取,只提供給一個使用者使用的。

而共用快取是放在伺服器上的,可以提供多個使用者使用。比如說某個比較熱點的視訊等熱點資源就會放在代理代理伺服器的快取中,以減低源伺服器的壓力,提升網路效率。

怎麼分辨這個資源是代理伺服器的快取還是源伺服器傳送的呢?

仍然使用掘金的例子

從圖中看出這個請求的Response Headers中的age頭部,單位是秒。

說明這個快取是共用快取返回的,age說明了它在共用快取存在的時間,圖中是327784,也就是在共用快取中存在了327784秒。

共用快取也有過期的時候,下面看看共用快取的工作原理。

如圖所示:
1、當client1發起請求時,Cache也就是代理伺服器(共用快取),轉發這條請求給源伺服器。源伺服器返回響應,並在Cache-Control頭部中設定可以快取100秒。接著在Cache中就會開啟一個定時器Age,將響應帶上Age:0頭部返回給client1

2、過了10秒後,client2傳送相同的請求,Cache中的快取還沒有過期,就帶上Age:10頭部返回快取中的響應給client2

3、過了100秒後,client3傳送同樣的請求,這時Cache中的快取已經過期了,就像前面說到那樣用條件請求頭部If-None-Match帶上快取的指紋發給源伺服器。當源服務認為此快取還能用,就返回304狀態碼給CacheCache就重新計時,從快取中找出響應帶上Age:0頭部返回給Client3

快取機制

HTTP協定中存在相關的快取機制,API中也可以直接使用這些機制來管理快取。HTTP的快取機制在RFC7234中進行了詳細的定義,分為:過期模型(Expiration Model)和驗證模型(Validation Model)兩類

  • 過期模型是指預先決定響應資料的儲存期限,當到達期限後就會再次存取伺服器端來重新獲得所需的資料
  • 驗證模型是指會輪詢當前儲存的快取資料是否為最新資料,並只在伺服器端進行資料更新時,才會重新獲得資料。

HTTP中,快取處於可用的狀態時稱為fresh(新鮮)狀態,而處於不可用的狀態時則稱為stale(不新鮮)狀態。

過期模型

過期模型可以通過伺服器的響應訊息裡包含何時過期的資訊來實現。HTTP1.1中定義了兩種實現方法:一個方法是用Cache-Control響應訊息首部,另一個方法就是用Expires響應訊息首部。

// 1
Expires: Fri, 01 Oct 2020  00:00:00 GMT
// 2
Cache-Control: max-age=3600複製程式碼

Expires首部從HTTP1.0就已經存在了,它是用絕對時間來表示到期,並使用RFC1123中定義的時間格式來描述。Cache-Control則是HTTP1.1中定義的表示從當前時間開始所經過的秒數。

這兩個首部該使用哪個,則是由返回的資料的性質決定的。對於一開始就知道在某個特定的日期會更新的資料,比如天氣預報這種每天在相同時間進行更新的資料,可以使用Expires首部來指定執行更新操作的時間。對於今後不會使用更新的資料或靜態資料等,可以通過指定一個未來非常遙遠的日期,使得獲取的快取資料始終儲存下去。但根據HTTP1.1的規定,不允許設定超過1年以上的時間,因此未來非常遙遠的時間最多也只能是1年後的日期了。

Expires: Fri, 01 Oct 2021  00:00:00 GMT複製程式碼

而對於不是定期更新,但如果更新頻率在某種程度上是一定的,或者雖然更新頻率不低但不希望頻繁存取伺服器端,對於這種情況可以使用Cache-Control首部。

如果ExpiresCache-Control首部同時使用時,Cache-Control首部優先判斷。

上面Cache-Control範例中使用到了max-age關鍵字,max-age計算會使用名為Date的首部。該首部用來顯示伺服器端生成響應資訊的時間資訊。從該時間開始計算,當經過的時間超過max-age值時,就可以認為快取已到期。

Date: Expires: Fri, 30 Sep 2020  00:00:00 GMT複製程式碼

Date首部表示伺服器端生成響應資訊的時間資訊。根據HTTP協定的規定,除了幾個特殊的情況之外,所有的HTTP訊息都要加上Date首部。

Date首部的時間資訊必須使用名為HTTP時間的格式來描述。在計算快取時間時,會用到該首部的時間資訊,這時就可以使用Date首部資訊來完成時間的同步操作,做到即便使用者端擅自修改日期等設定資訊。

驗證模型

與到期模型只根據所接收的響應資訊來決定快取的儲存時間相對,驗證模型採用了詢問伺服器的方式來判斷當前時間所儲存的快取是否有效。

驗證模型在檢查快取的過程中會不時地去存取網路。在執行驗證模型時,需要應用程式伺服器支援附帶條件地請求。附帶條件地請求是指前端向伺服器端傳送地「如果現在儲存地資訊有更新,請給我更新後地資訊」。在整個處理的過程中,前端會傳送同「過去某個時間點所獲得的資料」有關的資訊,隨後只有在伺服器端的資料發生更新時,伺服器端才會返回更新的資料,不然就只會返回304(Not Modified)狀態碼來告知前端當前伺服器端沒有更新的資料。

要進行附帶條件的請求,就必須向伺服器端傳達「前端當前儲存的資訊的狀態」,為此需要用到最後更新日期或實體標籤(Entity Tag)作為指標。顧名思義,最後更新日期表示當前資料最後一次更新的日期:而實體標籤則是表示某個特定資源版本的識別符號,十一串表示指紋印(Finger Print)的字串。例如響應資料的MD5雜湊值等,整個字串會隨著訊息內容的變化而變化。這些資訊會在伺服器端生成,並被包含在響應資訊的首部傳送給前端,前端會將其快取一同儲存下來,用於附帶條件的請求。

最後更新日期和實體標籤會被分別填充到Last-ModifiedETag響應訊息首部返回給前端

Last-Modified: Fri, 01 Oct 2021  00:00:00 GMT
ETag: 'ff568sdf4545687fadf4dsa545e4f5s4f5se45'複製程式碼

前端使用最後更新日期執行附帶條件的請求時,會用到Modified-Since首部。在使用實體標籤時,會用到If-None-Match首部

GET /v1/user/1
If-Modified-Since: Fri, 01 Oct 2021  00:00:00 GMT

GET /v1/user/1
If-None-Match: 'ff568sdf4545687fadf4dsa545e4f5s4f5se45'複製程式碼

伺服器端會檢查前端傳送過來的資訊和當前資訊,如果沒有發生更新則返回304狀態碼。如果有更新,則會同應答普通請求一樣,在返回200狀態碼的同時將更新內容一併返回給前端,這時也會帶上新的最後更新日期和實體標籤。當伺服器返回304狀態碼時,響應訊息為空,從而節約了傳輸的資料量。

HTTP協定中,ETag有強驗證與弱驗證兩個概念。

  • 執行強驗證的ETag
    ETag: 'ffsd5f46s12wef13we2f13dsd21fsd32f1'

  • 執行弱驗證的ETag
    ETag: W/'ffsd5f46s12wef13we2f13dsd21fsd32f1'

強驗證是指伺服器端同用戶端的資料不能有一個位元組的差別,必須完全一樣;而弱驗證是指即使資料不完全一樣,只要從資源意義的角度來看沒有發生變化,就可以視為相同的資料。例如廣告資訊,雖然每次存取時這些廣告的內容都會有所改變,但它們依然是相同的資源,這種情況下便可以使用弱驗證。

啟發式過期

HTTP1.1裡提到了當伺服器端沒有給出明確的過期時間時,使用者端可以決定大約需要將快取資料儲存多久。這時使用者端就要根據伺服器端的更新頻率、具體狀況等資訊,自行決定快取的過期時間,這個方法稱為啟發式過期。

例如前端通過觀察Last-Modified,如果發現最後一次更新是在1年前,那就意味著再將快取資料儲存一段時間也不會有什麼問題;如果發現到目前為止存取的結果是1天只有1次更新,那就意味著將快取儲存半天的時間或許可行。像這樣,前端能通過獨立判斷來減少存取次數。

雖然API是否允許使用啟發式過期的方法取決於API的特性,但由於伺服器端對快取的更新和控制理解最為深刻,因此伺服器端通過Cache-ControlExpires等準確無誤地向前端返回「將快取資料儲存多久」的資訊,對於互動雙方而言都是比較理想的做法。但如果不返回,伺服器端就需要通過Last-Modified等首部資訊來告知前端

使用Vary指定快取單位

在實施快取時可能還需要同時指定Vary首部。在實施快取時,Vary用於指定除URI外使用哪個請求首部專案來確定唯一的資料。使用Vary是因為即使URI相同,獲取的資料有時也會因請求首部內容的不同而發生變化。只有vary頭部指定的頭部必須與請求中的頭部相匹配才能使用快取。

vary的定義:

  • "*": 意味著一定匹配失敗
  • 1個或多個field-name:指定的頭部必須與請求中的頭部相匹配才能使用快取

如圖所示:
1、 當Client1攜帶Accept-Encoding:*頭部的GET請求傳送給serverserver返回的是gzip編碼的響應,以及vary:Content-Encoding頭部,表示著編碼方式一樣的時候才能使用快取。

2、當Client2攜帶Accept-Encoding:br頭部的GET請求傳送給server,這時請求的是br編碼。所以Cache不能使用快取,因為不匹配vary的中的值,只能轉發請求給源伺服器server

3、當Client3攜帶Accept-Encoding:br頭部的GET請求傳送給server,這時Cachebr編碼的快取,能匹配vary頭部的值,所以能使用快取返回。

一般而言,Vary首部用於HTTP經由代理伺服器進行互動的場景,特別是在代理伺服器擁有快取功能時。但是有時伺服器端無法得知前端的存取是否經由代理伺服器,這種情況下就需要用到伺服器驅動的內容協商機制,Vary首部也就成了必選項。

Cache-Control

Cache-Control頭部取值範圍非常複雜。

Cache-Control的定義是:

  • 必選的token
  • 可選的「=」,加上帶引號的值或者1個或多個十進位制的數位也就是指定的秒數

Cache-Control既可以在請求中使用,也可以在響應是使用。而且相同的值在請求和響應中的含義是不一樣的。

Cache-Control值有三種用法:

  • 1、直接使用token
  • 2、token值+ '=' + 十進位制數位
  • 3、token值+ '=' + 相應的頭部 / 直接使用token

在請求中的應用

在請求中Cache-Control的取值、用法及其含義:@後面表示第幾種用法

  • max-age@2: 告訴伺服器,使用者端不會接收Age超出max-age秒的快取
  • max-stale@2: 告訴伺服器,即使快取不再新鮮,但過期秒數沒有超過max-stale時,使用者端仍打算使用。若max-stale後沒有值,則表示無論過期多久,使用者端都可使用。
  • min-fresh@2: 告訴伺服器,Age至少經過min-fresh秒後快取才可使用
  • no-cache@1: 告訴伺服器,不能直接使用已有快取作為響應返回,除非帶著快取條件到上游伺服器得到304狀態碼才可使用現有快取。
  • no-store@1: 告訴各代理伺服器,不要對該請求的響應快取
  • no-transform@1: 告訴代理伺服器不要修改訊息包體的內容
  • only-if-cached@1: 告訴伺服器僅能返回快取的響應,否則若沒有快取則返回504錯誤碼

在響應中的應用

在響應中Cache-Control的取值及其含義:

  • max-age@2: 告訴使用者端快取Age超出max-age秒後則快取過期
  • s-maxage@2:與max-age類似,但僅針對共用快取,且優先順序高於max-ageexpires
  • must-revaildate@1: 告訴使用者端一旦快取過期,必須向伺服器驗證後才可使用
  • proxy-revalidate@1: 與must-revaildate類似,但它僅對代理伺服器的共用快取有效
  • no-cache@3: 1、告訴使用者端不能直接使用快取的響應,使用前必須在源伺服器驗證得到304返回碼。2、如果no-cache後指定頭部,則若使用者端的後續請求及響應中不含有這些頭部則可直接使用快取
  • no-store@1: 告訴所有下游伺服器但不能對響應進行快取
  • no-transform: 告訴代理伺服器不能修改訊息包體的內容
  • public@1: 表示無論私有快取或者共用快取,皆可將該響應快取
  • private@3: 1、表示該響應不能被代理伺服器作用共用快取使用。2、若priate後指定頭部,則告訴代理伺服器不能快取指定的頭部,可以快取其他頭部

相關免費學習推薦:(視訊)

以上就是一文搞定「快取」的詳細內容,更多請關注TW511.COM其它相關文章!