經過網絡獲取內容既速度緩慢又開銷巨大。較大的響應須要在客戶端與服務器之間進行屢次往返通訊,這會延遲瀏覽器得到和處理內容的時間,還會增長訪問者的流量費用。所以,緩存並重複利用以前獲取的資源的能力成爲性能優化的一個關鍵方面。css
私有緩存只能用於單獨用戶。你可能已經見過瀏覽器設置中的「緩存」選項。瀏覽器緩存擁有用戶經過 HTTP 下載的全部文檔。這些緩存爲瀏覽過的文檔提供向後/向前導航,保存網頁,查看源碼等功能,能夠避免再次向服務器發起多餘的請求。它一樣能夠提供緩存內容的離線瀏覽。html
共享緩存能夠被多個用戶使用。例如,ISP 或你所在的公司可能會架設一個 web 代理來做爲本地網絡基礎的一部分提供給用戶。這樣熱門的資源就會被重複使用,減小網絡擁堵與延遲。web
HTTP/1.1定義的 Cache-Control 頭用來區分對緩存機制的支持狀況, 請求頭和響應頭都支持這個屬性。經過它提供的不一樣的值來定義緩存策略。算法
緩存中不得存儲任何關於客戶端請求和服務端響應的內容。每次由客戶端發起的請求都會下載完整的響應內容。瀏覽器
Cache-Control: no-store
Cache-Control: no-cache, no-store, must-revalidate
以下頭部定義,此方式下,每次有請求發出時,緩存會將此請求發到服務器(譯者注:該請求應該會帶有與本地緩存相關的驗證字段),服務器端會驗證請求中所描述的緩存是否過時,若未過時(注:實際就是返回304),則緩存才使用本地緩存副本。緩存
Cache-Control: no-cache
"public" 指令表示該響應能夠被任何中間人(譯者注:好比中間代理、CDN等)緩存。若指定了"public",則一些一般不被中間人緩存的頁面(譯者注:由於默認是private)(好比 帶有HTTP驗證信息(賬號密碼)的頁面 或 某些特定影響狀態碼的頁面),將會被其緩存。性能優化
而 "private" 則表示該響應是專用於某單個用戶的,中間人不能緩存此響應,該響應只能應用於瀏覽器私有緩存中。服務器
Cache-Control: private
Cache-Control: public
過時機制中,最重要的指令是 "max-age=<seconds>",表示資源可以被緩存(保持新鮮)的最大時間。相對Expires而言,max-age是距離請求發起的時間的秒數。針對應用中那些不會改變的文件,一般能夠手動設置必定的時長以保證緩存有效,例如圖片、css、js等靜態資源。網絡
注:
當設置max-age時,empires頭會被忽略。
相應的,If-Modified-Since 只能夠用在 GET 或 HEAD 請求中,當與 If-None-Match 一同出現時,它會被忽略掉,除非服務器不支持 If-None-Match。函數
Cache-Control: must-revalidate
理論上來說,當一個資源被緩存存儲後,該資源應該能夠被永久存儲在緩存中。因爲緩存只有有限的空間用於存儲資源副本,因此緩存會按期地將一些副本刪除,這個過程叫作緩存驅逐。另外一方面,當服務器上面的資源進行了更新,那麼緩存中的對應資源也應該被更新,因爲HTTP是C/S模式的協議,服務器更新一個資源時,不可能直接通知客戶端及其緩存,因此雙方必須爲該資源約定一個過時時間,在該過時時間以前,該資源(緩存副本)就是新鮮的,當過了過時時間後,該資源(緩存副本)則變爲陳舊的。驅逐算法用於將陳舊的資源(緩存副本)替換爲新鮮的,注意,一個陳舊的資源(緩存副本)是不會直接被清除或忽略的,當客戶端發起一個請求時,緩存檢索到已有一個對應的陳舊資源(緩存副本),則緩存會先將此請求附加一個If-None-Match頭,而後發給目標服務器,以此來檢查該資源副本是不是依然仍是算新鮮的,若服務器返回了 304 (Not Modified)(該響應不會有帶有實體信息),則表示此資源副本是新鮮的,這樣一來,能夠節省一些帶寬。若服務器經過 If-None-Match 或 If-Modified-Since判斷後發現已過時,那麼會帶有該資源的實體內容返回。
下面是上述緩存處理過程的一個圖示:
對於含有特定頭信息的請求,會去計算緩存壽命。好比Cache-control: max-age=N的請求頭,相應的緩存的壽命就是N。一般狀況下,對於不含這個屬性的請求則會去查看是否包含Expires屬性,經過比較Expires的值和頭裏面Date屬性的值來判斷是否緩存還有效。若是max-age和expires屬性都沒有,找找頭裏的Last-Modified信息。若是有,緩存的壽命就等於頭裏面Date的值減去Last-Modified的值除以10(注:根據rfc2626其實也就是乘以10%)。
緩存失效時間計算公式以下:
expirationTime = responseTime + freshnessLifetime - currentAge
上式中,responseTime 表示瀏覽器接收到此響應的那個時間點。
更多地利用緩存資源,能夠提升網站的性能和相應速度。爲了優化緩存,過時時間設置得儘可能長是一種很好的策略。對於按期或者頻繁更新的資源,這麼作是比較穩妥的,可是對於那些長期不更新的資源會有點問題。這些固定的資源在必定時間內受益於這種長期保持的緩存策略,但一旦要更新就會很困難。特指網頁上引入的一些js/css文件,當它們變更時須要儘快更新線上資源。
web開發者發明了一種 Steve Sounders 稱做加速(譯者注:revving)的技術 。不頻繁更新的文件會使用特定的命名方式:在URL後面(一般是文件名後面)會加上版本號。加上版本號後的資源就被視做一個徹底新的獨立的資源,同時擁有一年甚至更長的緩存過時時長。可是這麼作也存在一個弊端,全部引用這個資源的地方都須要更新連接。web開發者們一般會採用自動化構建工具在實際工做中完成這些瑣碎的工做。當低頻更新的資源(js/css)變更了,只用在高頻變更的資源文件(html)裏作入口的改動。
這種方法還有一個好處:同時更新兩個緩存資源不會形成部分緩存先更新而引發新舊文件內容不一致。對於互相有依賴關係的css和js文件,避免這種不一致性是很是重要的。
加在加速文件後面的版本號不必定是一個正式的版本號字符串,如1.1.3這樣或者其餘固定自增的版本數。它能夠是任何防止緩存碰撞的標記例如hash或者時間戳。
Vary 是一個HTTP響應頭部信息,它決定了對於將來的一個請求頭,應該用一個緩存的回覆(response)仍是向源服務器請求一個新的回覆。它被服務器用來代表在 content negotiation algorithm(內容協商算法)中選擇一個資源表明的時候應該使用哪些頭部信息(headers).
在響應狀態碼爲 304 Not Modified 的響應中,也要設置 Vary 首部,並且要與相應的 200 OK 響應設置得如出一轍。
當緩存服務器收到一個請求,只有當前的請求和原始(緩存)的請求頭跟緩存的響應頭裏的Vary都匹配,才能使用緩存的響應。
使用vary頭有利於內容服務的動態多樣性。例如,使用Vary: User-Agent頭,緩存服務器須要經過UA判斷是否使用緩存的頁面。若是須要區分移動端和桌面端的展現內容,利用這種方式就能避免在不一樣的終端展現錯誤的佈局。另外,它能夠幫助google或者其餘搜索引擎更好地發現頁面的移動版本,而且告訴搜索引擎沒有引入Cloaking。
Vary: User-Agent
對於服務器而言, 資源文件可能不止一個版本, 好比說壓縮和未壓縮, 針對不一樣的客戶端, 一般須要返回不一樣的資源版本. 好比說老式的瀏覽器可能不支持解壓縮, 這個時候, 就須要返回一個未壓縮的版本; 對於新的瀏覽器, 支持壓縮, 返回一個壓縮的版本, 有利於節省帶寬, 提高體驗. 那麼怎麼區分這個版本呢, 這個時候就須要Vary了.
服務器經過指定Vary: Accept-Encoding, 告知代理服務器, 對於這個資源, 須要緩存兩個版本: 壓縮和未壓縮. 這樣老式瀏覽器和新的瀏覽器, 經過代理, 就分別拿到了未壓縮和壓縮版本的資源, 避免了都拿同一個資源的尷尬.
Vary:Accept-Encoding,User-Agent
如上設置, 代理服務器將針對是否壓縮和瀏覽器類型兩個維度去緩存資源. 如此一來, 同一個url, 就能針對PC和Mobile返回不一樣的緩存內容.
由於移動版和桌面的客戶端的請求頭中的User-Agent不一樣, 緩存服務器不會錯誤地把移動端的內容輸出到桌面端到用戶。
按照以上決策樹爲您的應用使用的特定資源或一組資源肯定最佳緩存策略。在理想的狀況下,您的目標應該是在客戶端上緩存儘量多的響應,緩存儘量長的時間,而且爲每一個響應提供驗證令牌,以實現高效的從新驗證。
不存在什麼最佳緩存策略。您須要根據通訊模式、提供的數據類型以及應用特定的數據更新要求,爲每一個資源定義和配置合適的設置,以及總體的「緩存層次結構」。
在制定緩存策略時,您須要牢記下面這些技巧和方法: