web性能優化--緩存

 什麼是緩存

緩存(Web緩存)是指代理服務器和客戶端本地磁盤保存的資源副本。當 web 緩存發現請求的資源已經被存儲,它會攔截請求,返回該資源的拷貝,而不會去源服務器從新下載。css

緩存大體能夠分爲私有緩存公有緩存。私有緩存只提供給單獨用戶使用,公有緩存能夠多個用戶都訪問使用。除了使用瀏覽器和代理緩存,還有網關緩存、CDN、反向代理緩存和負載均衡器等部署在服務器上。這裏主要說瀏覽器和代理緩存器。html

控制緩存的方式也不僅使用HTTP首部,可是其餘方式使用的很少,這裏只介紹首部控制。web

(公有)代理緩存

一般也叫代理緩存。緩存服務器是代理服務器的一種,並歸類在緩存代理類型中。當代理轉發從服務器返回的響應時,代理服務器將會保存一份資源的副本。瀏覽器

(私有)客戶端的緩存

緩存不只能夠存在於緩存服務器中,還能夠存在客戶端瀏覽器中。緩存

Web瀏覽器有內建的私有緩存——大多數瀏覽器都會將經常使用文檔緩存在我的電腦的磁盤或者內存中,而且容許用戶去配置緩存的大小和各類設置。服務器

緩存操做的目標

常見的HTTP緩存只能存儲GET響應。網絡

  • 一個檢索請求的成功響應:對於GET請求,響應狀態碼爲200
  • 永久重定向:響應狀態碼301
  • 錯誤響應:響應狀態碼404的一個頁面
  • 不徹底的響應:響應狀態碼206,只返回局部的信息
  • 除了GET請求外,若是匹配到一個一杯定義的cache鍵名的響應

緩存的處理過程

一下是對一條HTTP GET報文的基本緩存處理過程,這裏例子是一個新鮮命中的緩存。負載均衡

 圖片來自《HTTP權威指南》工具

  1. 接收——緩存從網路中讀取抵達的請求報文
  2. 解析——緩存對報文進行解析,提取出URL和各類首部
  3. 查詢——緩存查看是否有本地副本可用,若是沒有,去獲取並緩存。已緩存對象中包含了服務器響應主體和原始服務器響應首部,這樣就會在緩存命中時返回正確的服務器首部。
  4. 新鮮度檢測——緩存查看已緩存的副本是否新鮮,若是是,使用緩存響應客戶端的請求。若是不是,詢問服務器再驗證緩存的有效性。這裏涉及兩個很重要的內容:新鮮度和驗證機制,分別對應HTTP協議中規定的過時制度(Expiration Model)和驗證機制(Validation Model)。涉及的首部字段:Cache-Control(must-revalidate和max-age等)、Expires、Last-Modified、Etag等。
  5. 建立響應——緩存使用已緩存響應的首部做爲響應首部的起點,對這些首部進行修改和擴充。(關於使用緩存構建響應的詳細細節能夠去RFC文檔中查看)有可能爲了與客戶端匹配,修改http版本。已緩存響應的end-to-end首部被用來構造響應,好比新鮮度信息Cache-Control、Age以及Expires等。一般還會包含一個Via首部說明請求由代理緩存提供。
  6. 發送——緩存經過網絡將響應發回給客戶端
  7. 日誌——緩存可選地建立一個日誌文件條目來描述這個事務

緩存的控制

服務器經過HTTP定義的幾種方式來指定文檔過時以前能夠將其緩存多久。按照優先級遞減的順序,服務器能夠:佈局

  1. 附加一個Cache-Control: no-store首部到響應中;
  2. 附加一個Cache-Control:no-cache首部到響應中;
  3. 附加一個Cache-Control:must-revalidate首部到響應中;
  4. 附加一個Cache-Control:max-age首部到響應中;
  5. 附加一個Expires日期首部到響應中;
  6. 不附加過時信息,讓緩存肯定本身的過時日期

no-store:主要用於敏感信息,在請求或者響應中明確的要求不要緩存內容。

no-cache:標識爲no-cache的響應能夠緩存。可是在使用緩存以前必須向源服務器驗證其有效性。(標識爲no-cache的請求,標識客戶端不會接收緩存過的響應)

must-revaliable:標識爲must-revaliable的響應告訴緩存,必須跟源服務器驗證有效性。

max-age:從服務器將文檔傳來之時,能夠認爲文檔處於新鮮狀態的秒數。

Expires:指定一個絕對時間,在指定時間以前文檔是新鮮的。(不推薦,由於絕對時間沒有考慮時差等等因素)

新鮮度(過時時間)

服務器用HTTP/1.0+的Expires首部或HTTP/1.1的Cache-Control: max-age響應首部來指定過時日期,同時還會帶有響應主體。Expires首部和Cache-Control: max-age首部所作的事情本質上是同樣的,但因爲Cache-Control首部使用的是相對時間而不是絕對日期,因此咱們更傾向於使用比較新的Cache-Control首部。

若是Cache-Control和Expires同時存在,Cache-Control優先級更高。

若是首部不存在Cache-Control和Expires,則查找是否存在Last-Modified,若是有,緩存的壽命就等於頭裏面Date的值減去Last-Modified的值除以10(注:根據rfc2626其實也就是乘以10%)。

緩存失效時間計算公式以下:

expirationTime = responseTime + freshnessLifetime - currentAge

上式中,responseTime 表示瀏覽器接收到此響應的那個時間點。

加速資源

 對於設置了較長緩存時間的資源,一旦想要更新,會有些困難。網頁上引入js/css文件,當它們變更時須要儘快更新線上資源。

web開發者發明了一種被 Steve Souders 稱之爲 revving 的技術。不頻繁更新的文件會使用特定的命名方式:在URL後面(一般是文件名後面)會加上版本號。加上版本號後的資源就被視做一個徹底新的獨立的資源,同時擁有一年甚至更長的緩存過時時長。可是這麼作也存在一個弊端,全部引用這個資源的地方都須要更新連接。web開發者們一般會採用自動化構建工具在實際工做中完成這些瑣碎的工做。當低頻更新的資源(js/css)變更了,只用在高頻變更的資源文件(html)裏作入口的改動。

……具體細節待續

再驗證(校驗)

 若是文檔過時,並不意味着文檔與實際文檔有區別。只意味着緩存須要向源服務器驗證文檔的有效性。若是文檔確實發生了改變,緩存將獲取新的文檔替換舊的緩存。若是文檔未發生變化,緩存只須要換取新的首部,更新緩存對象的首部。

如何高效的實現再驗證呢?HTTP條件方法,容許發送一個「條件GET」,只有服務器文檔與緩存文檔不一樣時,纔會返回對象主體。

HTTP定義了5個條件請求首部。對緩存再驗證來講最有用的2個首部是If-Modified-Since和If-None-Match。

 

 If-Modified-Since後面能夠跟Date或者Last-Modified等日期。若是未發生變化,返回304,若是發生了變化,返回帶有新實體的200響應。

If-None-Match:Etag。若是Etag發生變化,返回帶有新實體的200響應。若是未發生變化,返回304。

兩種方法的選擇:服務器響應首部有Etag,則緩存使用If-None-Match再驗證。若是首部有Last-Modofied,則使用If-Modified-Since。因爲Etag是http1.1纔有的,若是兩種驗證都存在,就能夠同時保證http1.0和http1.1均可以進行驗證。

若是HTTP/1.1緩存或服務器收到的請求既帶有If-Modified-Since,又帶有實體標籤條件首部,那麼只有這兩個條件都知足時,才能返回304 Not Modified響應。

 Vary響應首部

代理服務器接收到源服務器返回的包含Vary指定項的響應以後,僅對請求中含有相同Vary指定首部字段的請求返回緩存。即便對相同的資源發出請求,若是Vary不一致,就必須從源服務器獲取資源。

使用vary頭有利於內容服務的動態多樣性。例如,使用Vary: User-Agent頭,緩存服務器須要經過UA判斷是否使用緩存的頁面。若是須要區分移動端和桌面端的展現內容,利用這種方式就能避免在不一樣的終端展現錯誤的佈局。

關於強制緩存和協商緩存

網上直接baidu瀏覽器緩存會出現一大堆文章都是關於瀏覽器緩存分爲強制緩存和協商緩存的。看了RFC文檔以及MDN相關文章,根本沒有出現這兩個單詞。之因此本身從新整理一篇文章主要是由於想要弄清楚緩存的原理,以及瀏覽器到底充當了什麼角色,再就是肯定強制緩存和協商緩存這兩個概念究竟是什麼。

所謂的強制緩存和協商緩存,只是HTTP首部的不一樣字段對應的不一樣功能。瀏覽器做爲用戶代理,處理HTTP請求和響應時根據HTTP首部採起不一樣的措施,本質上是由HTTP規範決定了瀏覽器採起哪些行動,而且非瀏覽器的用戶代理也會採起相同的措施,而且瀏覽器自己的功能分類。

因此我以爲這兩個概念並很差,突兀的告訴你,瀏覽器的緩存有兩個分類,對於概念的記憶和使用沒有多少好處。明白整個網絡通訊過程,以及緩存在其中的工做細節,纔是關鍵。

參考

  1. 《圖解HTTP》
  2. 《HTTP權威指南》
  3. RFC2616 (網上有不少翻譯的版本)
  4. HTTP緩存-MDN
  5. caching Tutorial
相關文章
相關標籤/搜索