rfc7234之http快取

2023-04-29 12:00:50

宣告:本人原創文章,詳細內容已釋出在我的微信個人技術公眾號---網路技術修煉,公眾號總結普及網路基礎知識,包括基礎原理、網路方案、開發經驗和問題定位案例等,歡迎關注。

快取概念

快取處理請求步驟

快取如果查詢到某個請求已經有快取,那麼需要進一步檢查該資源的新鮮度,根據新鮮度和請求中的欄位綜合評估是否要去伺服器端拉取新鮮的資源。

注意:

  • 建立響應時候要注意版本匹配,如果伺服器響應和使用者端請求的http版本不一致,要在快取伺服器做轉換。
  • 快取有通用的紀錄檔規範,常見的紀錄檔為Squid紀錄檔格式和網景的可延伸通用紀錄檔。
  • 快取中過期的資源也不一定要刪除,因為過期了也能用,刪除一般採用特定演演算法,如LRU。

以http get為例快取處理邏輯如下圖所示。

伺服器端響應寫入快取

相關http欄位

  • 快取伺服器如果不支援Range和Content-Range,那麼不要快取不完整的response。
  • 快取伺服器在未接收完一個Content-Range的content時候,不要給使用者端該部分的應答。
  • 快取伺服器可以將多個Content-Range的content片段組合到一起應答給使用者端。

快取響應的建立

Vary

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

新鮮度

相關http欄位

判斷一個緩衝是否過期可以使用以下公式:

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的計算

新鮮度生命時間(freshness_lifetime)表示資源從誕生到過期的相對時間(以秒為單位),其計算按照如下優先順序依次計算,如果某個優先順序的欄位符合計算條件則跳過後面的計算:

  • 共用式快取優先使用應答中的s-maxage。
  • 使用應答中的max-age。
  • 應答中的Expires減去Date。
  • 啟發式快取估算的時間(計算方式詳見:https://httpwg.org/specs/rfc7234.html)。

推薦使用Date和Last-Modified 計算的快取間隔時間除以10,即:

if ((last_modified > 0) && (date > 0) && (date - last_modified) > 0) {
    return (date - last_modified) / 10;
}

current_age的計算

current_age表示資源從誕生到現在的相對時間( 以秒為單位),其參與current_age的計算的因素有:

  • Age欄位表示實體從產生到現在以秒為單位經過多長時間了(多級快取的場景,上一級快取會填充該欄位)。
  • Date欄位表示報文內容在源伺服器中誕生的時間。
  • 主機時間。

計算方法:

方法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)

校驗

相關http頭

  • 條件驗證通過伺服器回覆304 Not Modified,再驗證實效返回200 ok。
  • 條件驗證標籤如果同時存在為「與」的邏輯關係,都滿足才能返回304 Not Modified。

快取控制

Cache-Control為http1.1上定義的快取控制策略。

TODO:請求中優先順序是什麼,比如no-cache和only-if-cached如果同時存在,需要去伺服器端條件get嗎?

其他

Pragma

Pragma: no-cache 含義與Cache-Control:no-cache相同,是為了相容http 1.0版本的欄位,當報文頭同時出現Cache-Control和Pragma時候,Pragma被忽略。

Cache Control Extensions

詳見:https://httpwg.org/specs/rfc7234.html

Warning

此資訊通常用於警告應用於訊息有效負載的快取操作或轉換可能引入的錯誤。詳細解釋見:https://httpwg.org/specs/rfc7234.html

Age

current_age的計算章節已經提到Age欄位表示實體從產生到現在以秒為單位經過多長時間了源站伺服器不會新增欄位,但是快取伺服器會新增,也為多級快取計算current_age提供了一個重要引數。詳見:https://httpwg.org/specs/rfc7234.html

Via

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