真正「搞」懂HTTP協定12之快取代理

2023-02-09 18:06:10

  我們在前兩篇的內容中分別學習了快取和代理,大致瞭解了快取有哪些頭欄位,代理是如何服務於伺服器和使用者端的,那麼把兩者結合起來,代理快取,也就是說代理伺服器也可以快取,當用戶端請求資料的時候,未必一定要追溯到源伺服器上,代理伺服器就可以直接把快取的資料返回給使用者端。並且,HTTP的快取,大多數其實都是由代理伺服器來實現,雖然源伺服器也有各種快取,比如大家可能聽過的Redis,還有Memcache、Varnish等等,但是基本上跟HTTP沒啥關係。

  如果沒有代理快取,代理伺服器僅僅只是一箇中專作用,轉發使用者端和伺服器的豹紋,中間不會儲存任何資料。但是一旦給代理加上快取後,事情就有些變化了。它在轉發報文的同時,還要把報文存入自己的Cache裡。下一次再有同樣的請求,代理伺服器就可以自己決斷,從自己的快取中取出資料返回給使用者端,就無需再去源伺服器獲取。這樣就降低了使用者的等待時間,同時也節約了源伺服器的網路頻寬。

  在HTTP的快取體系中,快取代理的身份十分特殊,它既是使用者端,又是伺服器,同時呢,它也既不是使用者端,又不是伺服器。因為代理面向使用者端,就是伺服器,面向伺服器就表現為使用者端,但是實際上代理又只是箇中轉,並不是真正的資料消費者和生產者,所以我們需要學一些新的Cache-Control屬性來對它做些額外的約束。

  嗯,這些新的屬性就是本篇的重點了。建議大家對比著快取那篇文章來看~

一、源伺服器的快取控制

  源伺服器的快取控制,額……原諒我重複了一遍標題,在有代理伺服器的場景下,它控制了哪些裝置或者說終端或者說使用者端呢?首先源伺服器鏈路前的所有裝置,包括代理伺服器,對於源伺服器來說都是使用者端。那麼問題來了,當有多個同樣角色的時候,我們要怎麼區分它們,針對不同的角色,設定不同的頭欄位屬性呢?

  我們在快取那一章所學的Cache-Control的四個屬性,對於代理伺服器來說也是可以使用的,但是使用者端和代理伺服器肯定是不一樣的,使用者端只是自己使用,但是代理伺服器,可能會分發給很多使用者端使用。所以,第一,我們要做的就是對代理伺服器和源伺服器的HTTP資料做不同的標識。

  首先,我們可以使用「private」和「public」來區分代理伺服器和使用者端上的快取。比如像cookie這種私有性很強的資料,只能儲存在使用者端,不然被黑進了代理獲取了使用者的私有資料那可是很嚴重的問題了。

  還有相對於「must-revalidate」是隻要快取過期,就要去源伺服器驗證,而「proxy-revalidate」只要求代理的快取過期後必須驗證,不必回溯到源伺服器,只驗證代理的快取就可以了。

  再次,快取的生存時間可以使用「s-maxage」,其實就是share-maxage的簡寫,用它來限定在代理上能夠存活多久,而使用者端扔就使用「max-age」。

  還有一個代理專用的屬性,「no-transform」。代理有的時候會對快取下來的資料做一些優化,比如把圖片生成如png、webp等各種格式,方便今後的請求,而「no-transform」就會禁止這樣的轉換。

  最後,大家一定要注意,源伺服器在設定完Cache-Control欄位後,必須要為報文加上「Last-Modified」或者「ETag」屬性,否則使用者端和代理後面就無法使用條件請求來驗證快取是否有效。

  最後的最後我們基於快取那一章的流程圖,把代理伺服器的驗證邏輯也加進去。

  我們仔細來看一下這張圖,在快取失效後的驗證節點,如果需要驗證的話,會額外的去檢視是否是代理快取,並決定後續是查詢代理還是查詢源伺服器。繼而判斷中間代理的快取邏輯,是private還是public。然後繼續判斷各自路徑的maxage。

  仔細看完這張圖後,我們發現,整個驗證的核心節點和關鍵步驟其實是沒有什麼變化的,只是在判斷的節點中額外的加入了是否需要代理的邏輯罷了。大家好好消化,仔細區分。

二、使用者端的快取控制。

  使用者端的快取控制相比於源伺服器的快取控制,在加入了代理的場景下要相對簡單一些。對於使用者端來說,代理伺服器就是伺服器,所以max-age、no-store、no-cache這三個屬性,跟面向源伺服器時是一樣的。

  但是關於快取的生存時間,在代理伺服器上的約束和條件則多了兩個新的屬性「max-stale」和「min-fresh」。

  「max-stale」的意思是如果代理上的快取過期了也可以接受,但不能過期太多,超過 x 秒也會不要。「min-fresh」的意思是快取必須有效,而且必須在 x 秒後依然有效。

  有的時候使用者端還會發出一個特別的「only-if-cached」屬性,表示只接受代理快取的資料,不接受源伺服器的響應。如果代理上沒有快取或者快取過期,就應該給使用者端返回一個 504(Gateway Timeout)。

  我們還是來看張圖鞏固下、串聯一下這些知識:

 

  這張圖就是完整的使用者端設定快取約束的判定流程,大家一定要對比著有代理和沒代理有啥區別來對照著學習。

三、小結

  代理伺服器可能會在響應報文中加入X-Cache、X-Hit等自定義的頭欄位,標識快取是否命中和命中率,方便觀察快取代理的工作情況。

  另外,大家還記得Vary這個頭欄位麼,我們在之前的章節中學到過,它是內容協商後的結果,相當於報文的一個版本標記,快取代理必須要儲存這些不同的版本,當再收到相同的請求時,代理就會讀取快取裡的Vary,對比請求頭裡的欄位判斷是否一致,是否可以返回快取的資料。

  還有一個「Purge」問題,也就是「快取清理」,他對於代理也是非常重要的功能,比如:過期的資料、版本老舊、快取危險資料等等。通常情況下,會使用一個自定義的PURGE方法,來刪除對應連線的快取資料。

  好啦~到這裡,我們學完了快取代理的相關頭欄位,其實並不怎麼複雜,只是在原有的快取的頭欄位的基礎上,加上了一些源伺服器和使用者端設定的頭欄位屬性,讓我們得控制快取的細粒度更精細一些。

  最後,我再次強調,大家要對比著和快取那一章節來看圖學習。

  有關於HTTP/1.1的內容基本上就告一段落了。下一篇我們一起去領略一下HTTP/2的風采。