宣告:本人原創文章,詳細內容已釋出在我的微信個人技術公眾號---網路技術修煉,公眾號總結普及網路基礎知識,包括基礎原理、網路方案、開發經驗和問題定位案例等,歡迎關注。
快取如果查詢到某個請求已經有快取,那麼需要進一步檢查該資源的新鮮度,根據新鮮度和請求中的欄位綜合評估是否要去伺服器端拉取新鮮的資源。
注意:
以http get為例快取處理邏輯如下圖所示。
Vary 是一個HTTP響應頭部資訊,它決定了對於未來的一個請求頭,應該使用一個快取作為響應還是向源伺服器請求一個新的響應。當響應中有vary欄位的時候,快取時候必須將vary欄位一併快取,下次請求的時候除了url外,還需要與vary快取的欄位完全一致才可以返回快取內容。
例如,如果響應的 Vary 欄位設定為 "Accept-Language",那麼在快取響應時,代理伺服器和使用者端應該按照請求中的 Accept-Language 欄位來區分不同的響應。這意味著,如果一個使用者端發出了兩個不同的請求,其中一個請求的 Accept-Language 欄位是 "en-US",另一個請求的 Accept-Language 欄位是 "fr-FR",那麼這兩個請求應該獲得不同的響應。
如果 Vary 欄位為空,那麼代理伺服器和使用者端可以快取響應,並在任何請求中重用它。
內容協商除了vary,主要靠Accept來實現,Accept 欄位,詳見下表:
請求頭欄位 |
說明 |
響應頭欄位 |
Accept |
告知伺服器傳送何種媒體型別 |
Content-Type |
Accept-Language |
告知伺服器傳送何種語言 |
Content-Language |
Accept-Charset |
告知伺服器傳送何種字元集 |
Content-Type |
Accept-Encoding |
告知伺服器採用何種壓縮方式 |
Content-Encoding |
判斷一個緩衝是否過期可以使用以下公式:
response_is_fresh=freshness_lifetime > current_age
其中freshness_lifetime為新鮮度宣告週期,current_age為快取已經生存的時間,這兩個值都是使用的相對時間,後面有詳細的計算方法。
注意:這個計算只能說明快取中的內容是否新鮮,具體能否直接將該內容回覆給使用者端,還需要結合請求頭中cache-control的max-age、max-stale、min-fresh欄位進行綜合判定。這個也比較好理解比如客戶購買了一個蘋果,吃的時候會看一下是否在保質期內,剛剛就是計算是否在保質期內,但不同人的處理可能不同,有的人發現過期兩天也會吃,有的人發現臨近過期就不吃了,請求中cache-control的max-age、max-stale、min-fresh欄位就代表了不同處理方式的人。綜上,關於快取伺服器對於一個存在的快取資源是否能直接返回給使用者端應該先計算freshness_lifetime - current_age,然後再綜合請求中cache-control判斷。
新鮮度生命時間(freshness_lifetime)表示資源從誕生到過期的相對時間(以秒為單位),其計算按照如下優先順序依次計算,如果某個優先順序的欄位符合計算條件則跳過後面的計算:
推薦使用Date和Last-Modified 計算的快取間隔時間除以10,即:
if ((last_modified > 0) && (date > 0) && (date - last_modified) > 0) { return (date - last_modified) / 10; }
current_age表示資源從誕生到現在的相對時間( 以秒為單位),其參與current_age的計算的因素有:
計算方法:
方法1: 用收到響應的時間減去Date欄位的值。
用response_time表示收到響應的時間,用date_value表示Date欄位的值,那麼
current_age = response_time - date_value
但是,接收端和源伺服器間很可能會有clock skew(時鐘偏差),為了防止這種情況,將負數結果賦值為0,所以該計算方案最終為:
current_age = max(0, response_time - date_value)
方法2:逐跳計算。
接收端收到響應報文時的Age值等於上一跳節點中快取的Age值加上傳輸時延。用previous_hop_age_value表示上一跳節點中快取對的Age值,用response_delay表示傳輸時延,那麼計算公式如下:
current_age = previous_hop_age_value + response_delay
respose_delay可以粗略地計算為得到響應時間減去發出請求的時間,這裡你可能會問,為什麼不要再除以二呢,因為HTTP對Age的計算策略是寧可多算也不肯少算的,多算頂多快取新鮮時間變短,產生額外的新鮮度驗證,但是少算的話,即使過期了,使用者端還會把它當成新鮮的用。
response_delay = response_time - request_time
這種方法的好處是response_time和request_time都是原生的時間,不存在時間偏差。
綜上所述,通常響應報文的計算會綜合上述兩種方法,取最大的一個。
#第一種計算方式 age_value_by_date = max(0, response_time - date_value) #第二種計算方式 response_delay = response_time - request_time age_value_by_hop = previous_hop_age_value + response_delay #兩種取大的 current_age = max(age_value_by_date, age_value_by_hop)
Cache-Control為http1.1上定義的快取控制策略。
TODO:請求中優先順序是什麼,比如no-cache和only-if-cached如果同時存在,需要去伺服器端條件get嗎?
Pragma: no-cache 含義與Cache-Control:no-cache相同,是為了相容http 1.0版本的欄位,當報文頭同時出現Cache-Control和Pragma時候,Pragma被忽略。
詳見:https://httpwg.org/specs/rfc7234.html
此資訊通常用於警告應用於訊息有效負載的快取操作或轉換可能引入的錯誤。詳細解釋見:https://httpwg.org/specs/rfc7234.html
current_age的計算章節已經提到Age欄位表示實體從產生到現在以秒為單位經過多長時間了源站伺服器不會新增欄位,但是快取伺服器會新增,也為多級快取計算current_age提供了一個重要引數。詳見:https://httpwg.org/specs/rfc7234.html
Via 是一個通用首部,是由代理伺服器新增的,適用於正向和反向代理,在請求和響應首部中均可出現。
Hypertext Transfer Protocol (HTTP/1.1): Caching https://httpwg.org/specs/rfc7234.html
Hypertext Transfer Protocol -- HTTP/1.1 https://www.rfc-editor.org/rfc/rfc2616
Hypertext Transfer Protocol (HTTP/1.1): Range Requests https://www.rfc-editor.org/rfc/rfc7233
《http權威指南》
HTTP中快取的使用期計算(Age Calculation)https://blog.csdn.net/sxh951026/article/details/77934463