淺談HTTP快取與CDN快取的那點事

2022-11-16 12:00:42

HTTP快取與CDN快取一直是提升web效能的兩大利器,合理的快取設定可以降低頻寬成本、減輕伺服器壓力、提升使用者的體驗。而不合理的快取設定會導致資源介面無法及時更新,從而引發一系列的衍生問題。本文將分別將從HTTP快取與cdn快取的規則、流程、設定入手,能讓大家瞭解基礎概念的同時,可對自己的專案設定客製化化的快取調優方案,以及在遇到快取問題時如何快速定位解決。

 

首先,讓我們來了解一下快取在實際場景中的應用

使用者第一次存取網站時,瀏覽器會從伺服器獲取所有的資源。在傳輸過程中,瀏覽器會通過一些約定好的響應頭,從而確定是否需要將這個資源儲存一份到本地作為快取,當用戶第二次存取該網站的時候,瀏覽器就會從快取中載入資源,不用向伺服器請求資源,從而提高了網站的存取速度,而若使用了CDN,當瀏覽器本地快取的資源過期之後,瀏覽器不是直接向源站點請求資源,而是向CDN邊緣節點請求資源,CDN邊緣節點中也存在快取,若CDN中的快取也過期,那就由CDN邊緣節點向源站點發出回源請求來獲取最新資源。

HTTP快取

簡介

http快取是一種使用者端快取,當Web瀏覽器向伺服器發起資源請求時,伺服器可以在響應報文頭中包含快取相關的資訊。這些HTTP Header會告訴瀏覽器是否以及如何快取資源,再次請求時如果命中快取將直接讀取本地快取不再發出請求。

快取規則

http快取規則由響應頭中Expires,Cache-Control ,Last-Modified ,Etag 這四個關鍵欄位控制。其中Expires和Cache-Control為強快取用來確定確定快取的儲存時間,Last-Modified 和Etag為協商快取則用來確定快取是否要被更新,接下來我們簡單來看一下區別。

強快取

•expires: HTTP1.0中用來控制快取時間的引數,header裡包含日期/時間,用GMT格式的字串表示, 即在此時間之後,響應過期。

•cache-control: HTTP1.1中用來控制快取時間的引數

◦public: 表明響應可以被任何物件(包括:傳送請求的使用者端,代理伺服器,等等)快取。

◦private: 表明響應只能被單個使用者快取,不能作為共用快取(即代理伺服器例如CDN不能快取它)。

◦max-age=<seconds>: 設定快取儲存的最大週期,相對於請求的時間快取seconds秒,在此時間內,存取資源直接讀取本地快取,不向伺服器發出請求。(與expires同時出現時,max-age優先順序更高)

◦s-maxage=<seconds>: 規則等同max-age,覆蓋max-age 或者 Expires 頭,但是僅適用於共用快取(比如各個代理),並且私有快取中它被忽略。(與expires或max-age同時出現時,s-maxage優先順序更高)

◦no-store: 不快取伺服器響應的任何內容,每次存取資源都需要伺服器完整響應

◦no-cache: 快取資源,但立即過期,每次請求都需要跟伺服器對比驗證資源是否被修改。(等同於max-age=0)

協商快取

•Last-modified: 源頭伺服器認定的資源做出修改的日期及時間。精確度比Etag低。包含有If-Modified-Since ( 資源修改的時間 )或 If-Unmodified-Since首部的條件請求會使用這個欄位,Last-Modified優先順序低於Etag。

•Etag: HTTP響應頭是資源的特定版本的識別符號,如果使用者端想再次請求相同的URL,將會傳送一個包含已儲存的ETag和「If-None-Match」(識別符號字串)欄位的請求。使用者端請求之後,伺服器可能會比較使用者端的ETag和當前版本資源的ETag(只要檔案內容改動,ETag就會重新計算)。如果ETag值匹配,這就意味著資源沒有改變,伺服器便會傳送回一個極短的響應,包含HTTP 「304 未修改」的狀態。304狀態告訴使用者端,它的快取版本是最新的,並應該使用它。

我們通過chrome控制檯可以很輕鬆的找到一個案例:

 

圖中設定

1.Cache-control: max-age=3600代表相對於請求時間,快取3600秒,即1小時,在此時間內,再次存取資源直接讀取本地快取,不向伺服器傳送請求.

2.Last-modified: Mon...上次修改時間,如果快取時間過期,該欄位將用於與請求中的If-Modified-Since欄位進行對比,一致則繼續使用之前快取,不一致則認定快取失效

3.Expires: 在http1.0版本下被cache-control覆蓋,此處意為快取至Mon, 07 Nov ...

4.Etag: Web伺服器會返回資源和其相應的ETag值,該欄位將用於與當前使用者端版本資源的ETag進行對比,一致則繼續使用之前快取,不一致則認定快取失效

快取流程

 

 

快取規則在其中是如何起作用的呢,我們來看幾個重點關注部分

重點關注1: 快取是否過期

基於該資源上次響應快取規則同時滿足下列條件則視為快取未過期,不發請求直接從本地快取讀取該檔案。需要注意的是,判斷快取是否過期只跟使用者端有關係,與伺服器端無關。1&2&3同時滿足即認為快取未過期,相反則是已過期

1.cache-control值為max-age

2.max-age > 0

3.當前 date < 上次請求時的date + max-age

注:如果HTTP為1.0時,則用expires判斷是否過期,如果HTTP為1.1及其以上時,則檢視cache-control。

重點關注2: 詢問伺服器資源是否修改

判斷資源是否修改,需要使用者端與伺服器共同共同作業,使用者端在首次拿到資源快取後會儲存Etag(若有)和Last-Modified(若有),在下次快取過期時會將Etag寫在請求頭部中的If-None-Match中,將Last-Modified值寫在請求頭部中的If-Modified-Since中,伺服器端優先對Etag進行對比,然後再對比Last-Modified,一致即視為快取沒有修改,命中協商快取,返回304,不一致則返回新檔案並帶上新的Etag或Last-Modified值。

重點關注3: 快取規則

參考上文快取規則,不在贅述。

 

小結

對於http快取的設定,我們只有在瞭解http快取的原理、規則、流程後,才能根據不同的情況客製化不同的規則,真正的發揮http快取在實際業務中的價值。

 

CDN快取

cdn快取是一種伺服器端快取,cdn服務商可以將源站上的資源緩到其各地的邊緣伺服器節點上。當用戶存取該資源時,cdn再通過負載均衡將使用者的請求排程到最近的快取節點上,有效減少了鏈路回源,提高了資源存取效率及可用性,降低頻寬消耗。

快取規則

與http快取規則不同的是,這個規則並不是規範性的,而是由cdn服務商來制定,我們以JD內部CDN舉例,開啟cdn接入介面,面板如下。

 


可以看到,提供給我們的設定項只有檔案型別(或檔案目錄)和Http2,在cdn節點上快取預設遵循源站設定快取時長。

運作流程

 

由圖我們可以看出CDN的主要處理邏輯集中在快取處理階段,除了關注CDN快取的檔案型別及時間外,我們還需要引入一個概念——回源,使用者端請求存取資源時,如果CDN節點上未快取該資源,或者部署預熱任務給CDN節點時,CDN節點會回源站獲取資源。如圖中所示,接入cdn後,我們提供服務的伺服器就是源站,源站一般情況下只會在cdn節點沒有資源或cdn資源失效時接收到cdn節點的資源請求,其他時間,源站並不會接收請求。簡單的概括就是,沒有資源就去源站讀取,有資源就直接傳送給使用者。值得注意的是cdn中有s-maxage=0、max-age=0、no-cache、no-store、private中的任一種時候,該型別檔案就被認定為不快取檔案,就是所有請求直接轉發源站,只有當快取時間大於0且快取過期的時候,才會與源站對比快取是否被修改。

快取設定

與在Web瀏覽器中的快取規則類似,可通過傳送快取指令檔頭來控制快取在CDN中的執行方式。儘管大部分檔頭最初都旨在解決使用者端瀏覽器中的快取問題,但現在所有中間快取(如 CDN)也會使用這些檔頭,可使用兩個檔頭來定義快取重新整理:Cache-Control 和 Expires。 如果兩者都存在,則 Cache-Control 為最新且優先於 Expires。 還有兩種用於驗證的檔頭型別(稱為驗證程式):ETag 和 Last-Modified。 如果兩者均已定義,則 ETag 為最新且優先於 Last-Modified。以OSS物件儲存為例,在快取設定的檔案中特別有以下說明。

快取繼承

當用戶請求您某一業務資源時,源站對應的Response HTTP Header中存在Cache-Control欄位,此時預設策略如下:

•Cache-Control欄位為max-age,對該資源的快取時間以設定的快取時間為主,對於小於1小時的快取時長,不繼承max-age指定時間。

•Cache-Control欄位為s-maxage=0、max-age=0、no-cache、no-store、private、nil或無 Cache-Control欄位時,物件儲存會源節點會為CDN預設新增:Cache-Control: max-age=3600頭部欄位,已確保提高快取的命中率,同時應對高並行回源流量帶來的風險與成本的增加。

快取影響

1.如果http快取設定cache-control: max-age=600,即快取10分鐘,但物件儲存cdn快取設定中設定檔案快取時間預設為1小時,那麼就會出現如下情況,檔案被存取後第20分鐘修改並上傳到伺服器,使用者重新存取資源,響應碼會是304,對比快取未修改,資源依然是舊的,一個小時後再次存取才能更新為最新資源

2.如果不設定cache-control呢,在http快取中我們說過,如果不設定cache-control,那麼會有預設的快取時間,但在這裡,物件儲存cdn服務商明確會在沒有cache-control欄位時主動幫我們新增cache-control: max-age=3600。

注:針對問題1,也並非沒有辦法,當我們必須要在快取期內修改檔案,並且不向想影響使用者體驗,那麼我們可以使用cdn服務商提供的強制更新快取功能,主要注意的是,這裡的強制更新是更新伺服器端快取,http快取依然按照http頭部規則進行自己的快取處理,並不會受到影響。

小結

cdn快取的設定並不複雜, 複雜的情況在於cdn快取設定會受到http快取設定的影響,並且不同的cdn運營商有各自的運營規則計費標準,結合來看才能讓cdn快取在業務中發揮最大的效能。

HTTP快取與CDN快取的結合

在我們分別瞭解http快取設定和cdn快取設定後,讓我們再結合引言看一次二者結合的請求過程

 

當用戶存取我們的業務伺服器時,首先進行的就是http快取處理,如果http快取通過校驗,則直接響應給使用者,如果未通過校驗,則繼續進行cdn快取的處理,cdn快取處理完成後返回給使用者端,由使用者端進行http快取規則儲存並響應給使用者。再回到開篇快取在實際場景中的應用,當我們分析快取問題時,一定要將兩個流程獨立開來分析判斷,是由於http快取設定的不合理,還是cdn快取未及時更新引起的問題。

 

實戰場景推薦(懶人版)

不同存取場景下的快取規則選擇

1.不更新檔案內容

優先使用http的本地快取,設定cache-control: max-age=seconds //seconds > 0(且設定為較大值31536000,即1年):強快取,快取當前資源,在設定時期內,再次請求資源直接讀取本地快取。

使用cdn快取,當本地快取無法使用時,設定較大的cache-control:同樣可以讓業務直接存取cdn資源,且設定時間內不會再發生回源請求。

2.很少更新檔案內容

對於img,css,js,fonts等非html資源,我們可以直接考慮設定cache-control: max-age=seconds //seconds > 0,並且max-age設定的時間可以相對久一些,類似於快取規則案例中,cache-control: max-age=36000設定10小時的快取,需要注意的是,這樣設定並不代表這些資源就一定十小時不變,其根本原因在於目前前端構建工具在靜態資源中都會加入戳的概念(例如,webpack中的[hash],gulp中的gulp-rev),每次修改均會改變檔名或增加query引數,本質上改變了請求的地址,也就不存在快取更新的問題。

3.頻繁更新檔案內容

對於html資源,作為前端資源的入口檔案,一旦被強快取,那麼相關的js,css,img等均無法更新。對於高頻維護的業務類專案,建議設定cache-control: no-cache或cache-control: max-age=0:採用協商快取,快取當前資源,但每次存取都需要跟伺服器對比,檢查資源是否被修改。但是基於流量和成本的考慮更推薦於max-age設定一個較小值,例如3600,一小時過期。對於一些活動專案,上線後不會進行較大改動,建議業務設定一個較小的max-age值,否則一旦出現bug或是未知問題,使用者無法及時更新。

除了以上考慮,有時候其他因素也會影響快取的設定,例如春晚紅包除夕活動,高並行大流量很容易給伺服器帶來極大挑戰,這時我們作為前端開發,就可以採用靜態頁面提前載入兜底來避免使用者多次進入帶來的流量壓力。

 

如何減少快取規則帶來的存取影響

1.通過清除快取控制

我們可以使用cdn服務商提供的強制更新快取功能,主要注意的是,這裡的強制更新是更新伺服器端快取,http快取依然按照http頭部規則進行自己的快取處理,並不會受到影響。

2.通過url帶版本號或者版本釋出時間

我們在使用物件儲存釋出資源時,可根據版本號或釋出時間定義url,以作為區分。

例:
https://storage.jd.com/xxx/xx?verson=1.1.1

例:
https://storage.jd.com/xxx/xx?verson=20221111

 

線上禁止的存取策略

對於存取同一資源,url帶時間戳、uuid等具有唯一性引數會直接回源至後端服務,帶來極高的頻寬成本及擊穿底層服務的風險,業務在使用CDN域名存取時,請務必謹慎使用帶有時間戳、唯一性引數的url。

 

總結

技術行業發展到今天,海量的流量已然成為常態,而http快取和cdn快取分別作為使用者端快取和伺服器端快取基石更是值得我們去深入學習、思考。

作者:管宸昊