淺談HTTP緩存

寫在前面:最近學習了修言同窗的小冊,受益良多。對於HTTP緩存這一塊,通過資料查詢和思考,也有了本身的一些思考認識,但願分享出來與你們一塊兒討論和成長。html

內容概述

  • 什麼是緩存及緩存的優勢
  • 緩存的處理步驟
  • 強緩存和協商緩存
  • 緩存決策
  • 總結與思考

1、緩存及其優勢

緩存

緩存是一種能夠自動保存常見資源副本並能夠在下一次請求中直接使用副本而非再次獲取的技術。前端

也就是說,當咱們首次進行資源請求以後,服務器在返回資源給客戶端的同時,緩存服務器或本地緩存也會保存一份資源副本(在容許緩存的狀況下),當咱們下次再對該資源進行請求時,則會直接使用資源副本而不會從原始服務器再次請求文檔。web

緩存的優勢

  1. 緩存能夠減小冗餘的數據傳輸。
  2. 緩存能夠緩解網絡瓶頸的問題。
  3. 緩存能夠下降對原始服務器的要求。
  4. 緩存能夠下降請求的距離時延。

數據的冗餘傳輸

當不少客戶端訪問同一份文檔的時候,原始服務器一遍又一遍地返回給不一樣的客戶端相同內容的文檔,這些重複的文檔形成了數據的冗餘傳輸。瀏覽器

網絡瓶頸問題

在大部分狀況下,客戶端訪問代理服務器的速度老是比訪問原始服務器更快(帶寬大、延遲低),所以若是代理服務器可以提供一份完整的副本,則遠遠比從原始服務器獲取來的快且省流量——尤爲針對大文件來講。緩存

下降原始服務器要求

突發事件(好比爆炸性新聞、某個名人事件)使不少人幾乎同時去訪問一個Web文檔時, 就會出現瞬間擁塞。由此形成的過多流量峯值可能會使網絡和Web服務器產生崩潰。使用緩存即可在必定程度上下降對原服務器的壓力。性能優化

下降請求距離時延

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

2、強緩存和協商緩存

一、 緩存相關概念解釋

緩存命中

若是某個請求的結果是由已緩存的副本提供的,被稱做緩存命中。網絡

緩存未命中

若是緩存中沒有可用的副本或者副本已通過期,則會將請求轉發至原始服務器,這被稱做緩存未命中 。frontend

新鮮度檢測

HTTP經過緩存將服務器文檔的副本保留一段時間。在這段時間裏, 都認爲文檔是「新鮮的」,緩存能夠在不聯繫服務器的狀況下,直接提供該文檔。但一旦已緩存副本停留的時間太長,超過了文檔的新鮮度限值(freshness limit), 就認爲對象「過期」了,在提供該文檔以前,緩存要再次與服務器進行確認,以查看文檔是否發生了變化。前端性能

再驗證

原始服務器上的內容可能會隨時變化,緩存須要常常對其進行檢測,看看它保存的副本是否還是服務器上最新的副本。這些新鮮度檢測被稱爲 HTTP 再驗證。

緩存能夠隨時對副本進行再驗證,但大部分緩存只在客戶端發起請求,而且副本舊得足以須要檢測的時候,纔會對副本進行再驗證。

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

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

二、 緩存的處理步驟

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

你們能夠先看幾遍這張圖,在腦海裏對緩存的過程有一個宏觀的瞭解,接下來我會對這張圖上的各個部分進行解讀。等到最後,再回來看這張圖便會以爲緩存是如此簡單的一個過程了。

三、 強緩存和協商緩存的概念

強緩存

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

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

協商緩存

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

協商緩存機制下,瀏覽器須要向服務器去詢問緩存的相關信息,進而判斷是從新發起請求、下載完整的響應,仍是從本地獲取緩存的資源。

四、 強緩存和協商緩存的實現原理

(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/’ 來標識):

五、 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的資源,都暗示着該資源具備敏感性信息。

六、優先級問題

(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是否也命中,只有當兩者都命中的狀況下,才從緩存中獲取緩存副本。

注意:此番觀點不知是否正確,但以爲這樣合乎情理,歡迎你們一塊兒討論或是指點一二。

3、緩存決策

因爲並未有過http緩存方面的實際應用經驗,在緩存決策方面實在沒有什麼本身的看法。

4、思考和總結

學習關於HTTP緩存方面的東西花費了很多時間,發現裏面的概念和知識點比較的雜亂,一開始沒法將整個緩存的過程串聯起來。經過對博客和書籍的查閱,終於梳理清楚了緩存的流程及各個步驟中涉及到的概念和知識點。既對整個流程有了清晰的把控,也能對流程的各個環節和細節有必定的瞭解。

思考了好久應該如何行文,纔可以既把大的流程講述清楚,又可以兼顧每一步涉及到的東西。 但願經過分享,可以給你們帶來一些新的東西和思考,那我也就知足了。

最後回憶一遍緩存的過程,在腦海裏畫出這張圖:

最後感謝你們的閱讀,辛苦啦^_^

若有錯誤,歡迎指正 weginjun@163.com

資料參考

相關文章
相關標籤/搜索