帶著問題學習:
- 緩存如何提高性能
- 如何衡量緩存的有效性
- 緩存置于何處作用最大
- HTTP如何保持緩存副本的新鮮度
- 緩存如何與其他緩存及服務器通信
web緩存是可以自動保存常見文檔副本的HTTP設備。
緩存優點
- 減少冗余的數據傳輸,節省網絡費用
- 緩解網絡瓶頸問題,更快加載
- 降低了對原始服務器的要求,服務器更快響應,避免過載
- 降低了距離時延
緩解網絡瓶頸
破壞瞬時擁塞
很多人同時訪問一個文檔,造成過多流量峰值,就會出現瞬時擁塞。
降低距離時延
假設Web頁面需要請求20個小圖片,支持4個并行連接,每個請求傳輸需要15ms(直線距離除光速傳輸速度換算而來),一個來回是30ms。全部請求完成至少需要多少時間?
答案如圖所示:
基于TCP慢啟動的特性,完全啟動4個并發連接需要的來回數:連接1 >> 連接2&3+圖片1 >> 連接4+圖片2&3 =3
剩余請求需要的來回數:(20+1-3)?4=5
(需要加上一個web基礎頁面)
因此總來回數是,3+5=8,至少需要8個完整來回,也就是8*30ms=240毫秒
總結一下完全啟動所需的連接并發數(N)最少需要幾個來回(m):m = N//2 + 1 if N%2 > 0 else N//2
(如果N不能整除2,就是N整除2加1個來回,否則是N整除2個來回)
命中與未命中
用已有的副本為某些到達緩存的請求提供服務,稱為緩存命中
。
其他一些到達緩存的請求沒有副本可用,而被轉發到原始服務端,稱為緩存未命中
。
HTTP再驗證
驗證緩存是否仍是服務器的最新副本,這些”新鮮度檢測
“被稱為HTTP再驗證。
再驗證過程:向原始服務器發送一個小的再驗證請求,內容無變化時服務端返回304,緩存標記為暫時新鮮,并將副本返回給客戶端,這被稱為再驗證命中
或緩慢命中
。
請求速度快慢:緩存命中 > 緩存再驗證命中 > 緩存再驗證未命中 ~ 緩存未命中
(成功的再驗證比未命中要快,省去了查詢數據和構建響應的過程,但失敗的再驗證幾乎與未命中速度一樣)
通常進行再驗證會添加If-Modified-Since
首部
再驗證未命中時,服務器會回送一條帶有完整內容的響應,供緩存更新。
若再驗證時發現對象被刪除,則服務器返回一個404,緩存收到會將其副本刪除。
(文檔)命中率
由緩存提供服務的請求所占的比例稱為緩存命中率
(或文檔命中率)。合理命中率約為40%。
字節命中率
由于文檔的尺寸不同,文檔命中率不能說明一切,更愿意使用字節命中率作為度量值。
字節命中率表示緩存提供的字節在傳輸的所有字節中所占的比例。100%字節命中率說明全部來自緩存。
區分命中和未命中
客戶端可以通過響應的Date
首部與當前時間判斷,日期早說明是緩存。或者通過Age
首部來檢測緩存的響應,可分辨出響應的使用期。
緩存的拓撲結構
單個用戶專有的緩存為私有緩存
,公共的緩存被稱為公有緩存
。
私有緩存
Web瀏覽器有內建的私有緩存,允許用戶自行配置。
公有緩存
公有緩存是特殊的共享代理服務器,被稱為緩存代理服務器
(代理緩存)。
代理緩存的層次結構
在緩存層次結構深的情況下,請求可能要穿過很長一溜緩存,每個攔截代理都會增加性能損耗。
各類型緩存
有些網格結構會構建復雜的網狀緩存
。網狀緩存中的代理緩存會進行更復雜的對話,做出動態緩存通信決策,決定與哪個父緩存對話,或者繞開緩存直接連接原始服務器,這種代理緩存可稱為內容路由器
。
內容路由器功能:選擇父緩存or原始服務器 >> 選擇特定父緩存 >> 在前往父緩存前搜索本地副本 >> 允許其他緩存對其緩存的部分內容進行訪問
緩存之間允許不同組織互為對等實體,提供可選的對等支持的緩存稱為兄弟緩存
,但HTTP并不支持兄弟緩存,所以有額外的協議對HTTP進行了擴展,比如因特網緩存協議ICP和超文本緩存協議HTCP。
緩存的處理步驟
大多數緩存都會保存緩存命中和未命中數據的統計數據,將條目插入一個用來顯示請求類型、URL和所發生事件的日志文件。
緩存處理流程圖
保持副本的新鮮
文檔過期首部
HTTP讓原始服務器向每個文檔附加一個過期日期Cache-Control
首部和Expires
首部
緩存過期前,可以以任意頻率使用這些副本,除非客戶端請求阻止提供已緩存或未驗證資源的首部,過期后必須與服務器進行核對。
過期日期和使用期
Cache-Control
首部使用的是相對時間而非絕對日期,絕對日期依賴計算機時鐘的正確設置,一般更傾向于用相對時間的Cache-Control
首部。
服務器再驗證
HTPTP協議要求行為正確的緩存返回下列內容之一:
- 足夠新鮮的已緩存副本
- 再驗證后仍然新鮮的已緩存副本
- 再驗證時發現原始服務器故障,返回一條錯誤報文
- 附有金高信息說明內容可能不正確的已緩存副本
用條件方法進行再驗證
HTTP定義了5個條件請求首部,最有用的是If-Modified-Since
(IMS)和If-None-Match
。
If-Modified-Since:Date再驗證
If-Modified-Since
為真表示文檔被修改了,服務器返回新首部的新文檔和新的過期時間給緩存,沒修改過為假,會返回一個新的過期日期。可以與Last-Modified
首部配合工作。
判斷的時候是將IMS日期于最后修改日期進行字符串匹配(即判斷更新日期是否一致),是“如果最后的修改不是在這個確定的日期進行的”,而不是“如果在這個日期之后沒有被修改過”。
If-None-Match:實體標簽再驗證
遇到下方的場景通常用If-None-Match
實體標簽再驗證:
- 有些文檔會被周期性寫入,內容沒有變化,但修改日期改變了
- 文檔可能被修改了,但該改動不重要
- 有些服務器無法準確判定其頁面的最后修改日期
- 有些文檔會以亞秒(一秒的十億分之一)間隙發生變化,以一秒為粒度的修改日期可能不夠用
If-None-Match
通過服務器返回的實體標簽(ETag
)再驗證。
也可以在If-None-Match
首部包含幾個實體標簽,逗號隔開,表示緩存已存在這些實體標簽的對象副本。
If-None-Match: "v2.6"
If-None-Match: "v2.4","v2.5","v2.6"
If-None-Match: "foobar","A34FAC0095","Profiles in Courage"
強弱驗證器
實體標簽和最近修改日期都是緩存驗證器
。HTTP/1.1支持弱驗證器,表示進行了少量修改,聲明是“足夠好”的等價體。服務器用“W/
”標識弱驗證器。
ETag: W/"v2.6"
If-None-Match: W/"v2.6"
什么時候使用實體標簽和最近修改日期
- 服務器只回送了一個
Last-Modified
最后修改日期,則客戶端用If-Modified-Since
驗證 - 服務器只回送了
Etag
實體標簽,客戶端用If-None-Match
驗證 - 實體標簽和最后修改日期都提供了,則都進行驗證
控制緩存的能力
緩存多長時間,按照優先級遞減的順序,服務器可以:
- 附加一個
Cache-Control: no-store
首部到響應中去; - 附加一個
Cache-Control: no-cache
首部到響應中去; - 附加一個
Cache-Control: must-revalidate
首部到響應中去; - 附加一個
Cache-Control: max-age
首部到響應中去; - 附加一個
Expires
日期首部到響應中去; - 不附加過期信息,讓緩存確定自己的過期日期。
no-store
首部(禁止復制響應)和no-cache
首部(新鮮度再驗證前不能返回緩存)可以防止緩存提供未經證實的已緩存對象。
must-revalidate
表示嚴格遵循過期時間,到期要進行再驗證,驗證通過才發送副本。
max-age
最長可以處于新鮮狀態的秒數,為0則不緩存,如:Cache-Control: max-age=0
。s-maxage
類似,但僅適用于公有緩存。
不推薦使用Expires
絕對日期作為過期日期
試探性過期
服務器沒有提供過期相關的首部,緩存可以使用任意算法(比如LM-Factor)計算出一個試探性的最大使用期,但如果大于24小時,則應該添加一個試探性過期警告13。(默認的新鮮周期通常是一小時或一天)