標籤:副本 原因 等等 tom 網域名稱 dia time 效果 語言
原文地址:http://tools.ietf.org/html/rfc2616#section-14.9
本文內容
概述
術語
HTTP Cache-Control 頭
可快取的資源
可被快取儲存的資源
修改基本到期機制
緩衝重新驗證和重新載入的控制
no-transform 指令
緩衝控制擴充
參考資料
概述
最近做的項目使用了 Ext.Net,由於之前看了很多介紹提高網站效能的文章,這類文章大多趨於理論,或是工程實踐,都是三言兩語,只能意會,沒個直觀的理解。很多東西還得靠自己。
開始關注效能問題源於我畢業之初做的一個項目,當時沒什麼經驗,只是一味寫代碼,功能和設計至上,完全沒考慮效能問題。另一部分原因是當時對 Web 應用程式的很多實現細節還不甚瞭解。知道 Why 和 What,但不知道 How。
這段時間,閑來無事,就針對 Ext.Net Web 應用程式利用網頁測試和統計工具,比如 HttpWatch、YSLOW 等工具進行了一些測試。這些測試著實花了我很多時間,但收穫巨大。
本文針對利用 HTTP 頭 Cache-Control 的緩衝問題。之前在網和 MSDN 上搜,沒找到幾個關於 Cache-Control 頭的可能賦值,後來直接去找 RFC 文檔,才豁然開朗。
以下是 RFC 2616 文檔關於 Cache-Control 的部分,RFC 文檔對一些語氣詞,如 "MUST", "MUST NOT" 等,以及術語都做了明確的規定。本文文字實在太多,也不容易翻譯。因為 RFC 文檔側重理論,使用的概念要比通常的叫法更廣。比如用實體可以涵蓋很多東西,頁面、CSS、指令碼、映像等等都可以認為是實體,但本文把它們當作是實體體,而封裝後的實體體才叫實體。除了實體,還區分了實體體,實體體是實體的一部分,可以看作是實體的內容;資訊概念更側重於 HTTP 協議裡邊的相關東西;另外,區分了伺服器(server)和原始伺服器(origin server)。
術語
以下是本文需要的術語(RFC 2616 文檔還有其他一些術語)。本來不想列出來的,但想下還是有必要的——瞭解一個事物,首要的是能區分概念。
訊息(message)
HTTP 通訊的基本單元,它由一個結構化八進位序列組成,包括訊息頭、訊息體、訊息長度、通用訊息頭域等等(參看 RFC 2616 的 4 節),並通過網路傳輸。
請求(request)
一個 HTTP 要求資訊。
響應(response)
一個 HTTP 響應資訊。
資源(resource)
可以由 URI(Uniform Resource Identifier)識別的一個網路資料對象或服務(參看 RFC 2616 的 3.2 節)。資源可以可靠的方式進行多種呈現(例如,多語言,資料格式、大小和解析),或是其他變化。
實體(entity)
資訊作為一個請求或響應的有效載荷(payload)來傳輸。一個實體由以實體頭域形式的元資訊和以實體體形式的內容所組成。
實體(entity)即 HTTP 協議,實體頭域(entity-header fields )即 HTTP 頭,而實體體(entity-body)即 HTTP 體。
有效載荷的意思是,通常在傳輸資料時,為了使資料轉送更可靠,要把未經處理資料分批傳輸,並且在每一批資料的頭和尾加上一定輔助資訊,比如該批資料量的大小,校正位等,這樣為封裝未經處理資料,使未經處理資料不易丟失,形成了傳輸通道中基本的傳輸單元——資料幀或資料包。這些資料幀中的未經處理資料就是有效載荷資料。
用戶端(client)
一個為發送請求建立串連的程式。
使用者代理程式(user agent)
初始化一個請求的用戶端。這通常是瀏覽器、編輯器、網路蜘蛛(網頁抓取程式),或其他的終端使用者工具。
伺服器/伺服器端(server)
一個接收連線應用程式程式,通過發迴響應服務於請求。任何給定的程式都可能既是一個用戶端,也是一個伺服器。我們使用的這些術語僅指由程式執行一個特定串連的角色,而不是通常程式的功能。同樣,基於每個請求的性質來切換行為,任何伺服器都可以作為原始伺服器(origin server)、代理(proxy)、網關(gateway),或是管道(tunnel)。
伺服器端或用戶端主要是指它們在網路中所處的角色。
原始伺服器(origin server)
駐留或建立一個給定資源的伺服器。
換句話說,原始伺服器是儲存資源的地方,比如存放靜態資源的伺服器,存放 Web 應用程式的伺服器等,出於提高用戶端響應速度的考慮,會將靜態資源單獨放到一個網域名稱下。
代理(proxy)
一個作為伺服器端和用戶端,代表用戶端發出請求的中間程式。請求在內部處理,或是通過翻譯,把它們發送到其他伺服器。代理必須要同時實現這個規範的用戶端和伺服器端需求。一個“透明代理”是一個不修改超出Proxy 驗證和識別要求的請求或響應的代理。一個“非透明代理”是一個修改請求或響應,為使用者代理程式提供額外服務(增值服務)的代理,例如組注釋服務,媒體類型轉換,協議簡化,或匿名過濾。除了透明或不透明的行為要明確指出外,HTTP 代理的需求要同時適用於兩種類型的代理。
網關(gateway)
一個作為處理其他服務的中介的伺服器。與代理不同,網關接收請求,就好像它是為請求資源的原始伺服器;請求的用戶端並沒有意識到,它們正在與網關通訊。
也就是說,網關與代理都接收使用者的請求。往往終端使用者並沒有意識到,其他它們正在與網關通訊。
可緩衝(cacheable)
如果一個緩衝被允許儲存響應資訊的副本,以便在回答接下來的請求中使用,那麼一個響應就是可快取的。確定 HTTP 響應緩衝能力的規則參看 RFC 2616 的13 節。即使一個資源是可快取的,也可能有其他限制,如一個緩衝是否可以對一個特定請求使用已緩衝的副本。
顯式到期時間(explicit expiration time)
這個時間是,原始伺服器計劃,一個實體無需進一步驗證不再從緩衝返回。
也就是說,當伺服器發現請求中的到期時間表明,使用者緩衝中的資源已經到期,那就不能再從緩衝獲得資源。
絕對時間(age)
響應的絕對時間是,是自被原始伺服器發送,或成功驗證的開始時間。
響應生命週期(freshness lifetime)
一個響應的產生與它到期時間之間的時間長度。
新的(fresh)
如果一個響應的年齡還沒有超過它的響應生命週期,那麼這個響應就是新的。
陳舊(stale)
如果一個響應已經超過它的響應生命週期,那麼這個響應就是陳舊。
驗證器(validator)
一個用於檢查一個緩衝條目是否為一個等價的實體副本的協議元素,例如一個實體標籤(Etags),或是最後修改時間。
用於驗證一個實體是否到期的方法。
請求/響應鏈(request/response chain)
request chain ------------------------>
UA -------------------v------------------- O
<----------------------- response chain
HTTP Cache-Control 頭
Cache-Control 頭用於指定緩衝指令,所有請求/響應鏈的緩衝機制必須遵守這個指令。該指令規定行為,意在防止緩衝受到請求或響應的不利幹擾。通常,這些指令可以覆蓋預設的緩衝演算法。緩衝指令是單向的,也就是說,在一個請求中存在緩衝指令不意味著也在其響應中存在。
注意:HTTP/1.0 緩衝沒有實現 Cache-Control,只實現了 Pragma: no-cache。
不管緩衝指令(Cache directives)對應用程式意義如何重大,它們必須通過代理(瀏覽器)或是網關應用程式傳遞,因為該指令可以應用與請求/響應鏈上的所有接收者。為一個特定的緩衝規定緩衝指令是不可能的。下面是 Cache-Control 的可能值。
Cache-Control = "Cache-Control" ":" 1#cache-directive
cache-directive = cache-request-directive
| cache-response-directive
cache-request-directive =
"no-cache"
| "no-store"
| "max-age" "=" delta-seconds
| "max-stale" [ "=" delta-seconds ]
| "min-fresh" "=" delta-seconds
| "no-transform"
| "only-if-cached"
| cache-extension
cache-response-directive =
"public"
| "private" [ "=" <;"> 1#field-name <"> ]
| "no-cache" [ "=" <;"> 1#field-name <"> ]
| "no-store"
| "no-transform"
| "must-revalidate"
| "proxy-revalidate"
| "max-age" "=" delta-seconds
| "s-maxage" "=" delta-seconds
| cache-extension
cache-extension = token [ "=" ( token | quoted-string ) ]
當指令中沒有 1#field-name 欄位名參數時,指令應用於整個請求或響應。當出現 1#field-name 參數時,它僅僅應用於命名的欄位,而不會應用於請求或響應的其餘部分。該機制支援擴充,以適應未來 HTTP 協議。
cache-control 指令,即以上可能的值,可以被劃分成以下幾類:
可緩衝資源的限制。只能由原始伺服器完成。
可被緩衝儲存的限制。可以由原始伺服器或使用者代理程式完成。
修改基本的到期機制。可以由源服務或使用者代理程式完成。
控制緩衝重新驗證和重新載入。只能由使用者代理程式完成。
控制實體傳輸。
擴充緩衝系統。
可快取的資源
預設情況下,如果要求方法、要求標頭部和響應狀態的需要指示是可快取的,那麼一個響應就是可快取的。RFC 2616 的 13.4 節概述了預設的緩衝功能。下面 Cache-Control 響應指令允許原始伺服器覆蓋一個響應預設的緩衝功能:
public
指示響應可以被任何緩衝所緩衝,即使通常它只是非可緩衝或可緩衝到一個非共用快取內。(參考 RFC 2616 14.8 節 授權))
private
指示響應資訊的全部或部分用於單個使用者,而不能用一個共用快取來緩衝。這可以讓原始伺服器指示,響應的特定部分只用於一個使用者,而對其他使用者的請求則是一個不可靠的響應。一個 private(非共用)緩衝可以緩衝這樣的響應。
注意:使用 private 僅僅控制可以緩衝響應的哪裡,不能保證資訊內容的隱私。
no-cache
如果 no-cache 指令沒有規定 field-name,那麼一個緩衝不能使用響應以滿足接下來的、沒有與原始伺服器重新驗證的請求。這可以讓原始伺服器防止緩衝,甚至是已被配置的緩衝,返回給用戶端陳舊的響應。
如果 no-cache 指令規定了一個或多個 field-names,那麼一個緩衝可以使用響應來滿足接下來的請求,遵守緩衝的其他限制。然而,指定的 field-name 參數不能在響應中被發送給接下來的、沒有與原始伺服器成功重新驗證的請求。這可以讓原始伺服器防止重用響應中的某個頭,而仍然可以緩衝響應的其他部分。
可被緩衝儲存的資源
no-store
no-store 指令的目的是防止無心發布或是保留了敏感資訊(例如,備份)。no-store 指令應用於整個資訊,可以在響應或請求中發送。如果是在一個請求中發送,那麼緩衝不能儲存這個請求或任何響應的任何部分給它。如果在一個響應中發送,那麼緩衝不能儲存它引起的響應或請求的任何部分。這個指令可以應用於共用或非共用快取。在上下文環境中,“不能儲存”意思是緩衝不能把資訊有意地儲存在非易失行性儲器上,而且,在使用後,必須盡最大努力從易失儲存上儘可能快地刪除資訊。
即使該指令與一個響應一起使用,使用者也可能會顯式地把這個響應儲存到緩衝系統之外(例如,"Save As" 對話方塊,或“匯出”)。記錄緩衝可以把響應作為其正常操作的一部分來儲存。
該指令的目的是為了滿足某些使用者和服務作者指定的要求,他們關心的是,通過意外地訪問緩衝的資料結構,導致的資訊意外釋放。當使用該指令可以在某些情況下提高隱私,但是需要注意的是,在某種程度上,它是不可靠的,或者說,是個不足以確保隱私的機制。特別是,惡意的緩衝可能無法識別或遵守這個指令,這樣通訊網路很容易會被竊聽。
修改基本的到期機制
實體的到期時間可以由原始伺服器通過 Expires 頭來指定(參考 RFC 2616 的 14.21 節)。另一個方法是,在響應中使用 max-age 指令。當一個已緩衝的響應中存在 max-age 緩衝指令時,如果當前的絕對時間大於一個新請求該資源的給定時間值,那麼該響應就是陳舊的。響應中的 max-age 指令意味著,響應是可快取的(即,"public"),除非其他的,還有更限制的緩衝指令。
如果一個響應既包含 Expires 頭,又包含 max-age 指令,那麼 max-age 指令會覆蓋 Expires 頭,即使 Expires 頭更有限制性。這個規則允許原始伺服器,對於一個給定響應,向 HTTP/1.1(或之後)緩衝比 HTTP/1.0 提供一個更長的到期時間。如果某個 HTTP/1.0 緩衝由於不同步的時鐘而不當地計算絕對時間或到期時間,那麼這個就會很有用。
很多 HTTP/1.0 緩衝的實現會把小於等於響應日期值的到期值當作等價於 Cache-Control 響應指令 "no-cache"。如果一個 HTTP/1.1 緩衝接收到這樣的響應,並且響應不包含 Cache-Control 頭,那麼它會考慮把響應作為不可緩衝,以便同 HTTP/1.0 相容。
注意:原始伺服器可能希望在一個包含不能理解該指令的舊緩衝的網路中使用一個相對較新的 HTTP 緩衝控制功能,如 "private" 指令。原始伺服器會把這個新功能與到期結合起來,該到期的值小於等於日期值。這將防止陳舊的緩衝不當地緩衝的響應。
s-maxage
如果一個響應包含 s-maxage 指令,那麼對於共用快取(而不是對私人緩衝),由該指令規定的最大絕對時間會覆蓋由 max-age 指令或 Expires 頭規定的最絕對時間。s-maxage 指令也隱含 proxy-revalidate 指令的語義(將在本文“控制緩衝重新驗證和重新載入”小節介紹),也就是說,當共用快取對接下來的請求的響應變得陳舊後,該請求沒有與原始伺服器重新驗證,共用快取不能使用緩衝條目。私人緩衝總是忽略 s-maxage 指令。
注意,大多數與上面規範不相容的舊緩衝沒有實現任何 cache-control 指令。希望使用 cache-control 指令來限制的原始伺服器,而不妨礙 HTTP/1.1-compliant 緩衝,可以使用 max-age 指令覆蓋 Expires 頭,事實上,HTTP/1.1-compliant 之前的緩衝不檢查 max-age 指令。
其他指令可以讓使用者代理程式修改基本的到期機制。這些指令可以在一個請求中規定:
max-age
指示用戶端願意接收其絕對時間不大於指定的時間,以秒計。除非還包含 max-stale 指令,否則用戶端不期望接收一個陳舊的響應。
min-fresh
指示用戶端願意接收一個其響應生命週期不小於它當前絕對時間,再加上指定的時間的響應,以秒計。也就是說,用戶端想要的一個響應,至少在指定的秒數是新的。
max-stale
指示用戶端願意接收一個已經超過其到期時間的響應。如果 max-stale 被分配一個值,那麼用戶端願意接收已經超過其到期時間,不超過指定的秒數。如果沒有分配給 max-stale 值,那麼用戶端願意接收一個任何絕對時間的陳舊的響應。
如果緩衝返回一個陳舊的響應,無論是因為一個請求中的 max-stale 指令,還是因為緩衝被配置為覆蓋一個響應的到期時間,那麼,緩衝必須把一個警告頭 110 加到這個陳舊的響應。
一個緩衝可以被配置為返回陳舊的響應,無需驗證,但只有與任何需要 "MUST層級" 的緩衝驗證不衝突時(例如,一個 "must-revalidate" 緩衝控制指令)。
如果這新請求和已緩衝的條目都包含 "max-age" 指示,那麼這兩個值中較小的那個用於為請求確定已緩衝條目的新的程度。
控制緩衝重新驗證和重載入
有時,使用者代理程式可能想或需要堅持,一個緩衝與原始伺服器(不僅僅沿著原始伺服器路徑的下一緩衝)重新驗證它的緩衝條目,或是從原始伺服器重新載入緩衝條目。如果緩衝或原始伺服器已過高的估計已緩衝的響應的到期時間,那麼可能需要點對點重新驗證。如果緩衝條目處於某種原因已經完全沒有用處,那麼可能需要點對點重新載入。
可以請求點對點重新驗證,當用戶端沒有屬於自己的本地已緩衝的副本時,稱為 "unspecified end-to-end revalidation",或是當用戶端沒有本地已緩衝的副本時,稱為 "specific end-to-end revalidation"。
用戶端通過 Cache-Control 請求指令可以規定三種動作:
End-to-end reload
請求包含 "no-cache" 緩衝控制指令,或是為了與 HTTP/1.0 用戶端相容的 "Pragma: no-cache"。請求中的 no-cache 指令不能包含 field-name。當響應這樣一個請求時,伺服器不能使用已緩衝的副本。
Specific end-to-end revalidation
請求包含一個 "max-age=0" 緩衝控制指令,這會迫使每個沿著原始伺服器路徑的緩衝,如果有,重新驗證它自己的條目。初始請求包含一個帶用戶端當前的驗證器的緩衝驗證條件。
Unspecified end-to-end revalidation
請求包含一個 "max-age=0" 緩衝控制指令,這會迫使每個沿著原始伺服器路徑的緩衝,如果有,重新驗證它自己的條目。初始請求不包含緩衝驗證條件;擁有此資源緩衝條目的第一個沿路徑的緩衝(如果有)包含一個帶用戶端當前的驗證器的緩衝驗證條件。
max-age
當利用 max-age=0 指令迫使一個中間緩衝重新驗證它自己的緩衝條目,並且用戶端已經在請求中提供它自己的驗證器,那麼,這個所提供的驗證器可能與當前儲存緩衝條目的驗證器不同。這種情況下,在不影響語義的情況下,緩衝也可以使用它自己的請求中的驗證器。
然而,驗證器的選擇可能會影響效能。對中間緩衝(代理)來說,最好的方法是當自己發出請求時,使用它自己的驗證器。如果伺服器響應 304(Not Modified),那麼緩衝可以用一個 200 響應返回它已經驗證的副本給用戶端。然而,如果伺服器用一個新的實體和緩衝驗證器回應,那麼,中間緩衝使用強比較函數把返回的驗證器與用戶端請求中提供的進行比較。如果用戶端驗證器與原始伺服器的相等,那麼中間緩衝(代理)就簡單地返回 304((Not Modified)響應。否則,用一個 200(OK)響應返回新的實體。
如果一個請求包含 no-cache 指令,那麼它不就應該包含 min-fresh、max-stale 或 max-age。
only-if-cached
在某些情況下,如網路連接非常差時,用戶端可能需要一個緩衝,只返回目前已儲存的那些響應,而不是重新載入,或與原始伺服器重新驗證。要做到這一點,用戶端可以在一個請求中包含 only-if-cached 指令。如果伺服器接收到這個指令,緩衝應該,或是使用與其他要求節流一致的快取項目響應,或是用 504(Gateway Timeout)響應。但是,如果一個緩衝組在一個統一的具有良好的網路連接的系統內被操作,這樣一個請求可能會被在緩衝組內轉寄。
must-revalidate
因為緩衝可以配置成忽略伺服器指定的到期時間,並且,因為一個用戶端請求可以包含 max-stale 指令(具有類似的效果),對原始伺服器,協議還包括一個機制,需要在接下來的使用中重新驗證緩衝條目。當 must-revalidate 指令存在於一個已被緩衝收到的響應時,響應接下來沒有與原始伺服器初次重新驗證的請求的條目變舊之後,緩衝不能使用該條目。(即,在只基於原始伺服器 Expires 頭或 max-age 值,如果已緩衝的響應舊了,那麼緩衝必須每次完成一個點對點的重新驗證。)
must-revalidate 指令對於某些協議功能的可靠運行是必需的。在任何情況下,HTTP/1.1 緩衝必須遵守 must-revalidate 指令;特別是,如果緩衝出於某種原因不能到達原始伺服器,那麼它必須產生 504(Gateway Timeout)響應。
伺服器應該發送 must-revalidate 指令,若且唯若沒有成功重新驗證一個實體的請求會導致不正確的操作,例如一個靜默未執行的金融事務。接收者不能自動執行任何違反該指令的動作,並且,如果重新驗證失敗,不能自動提供一個未驗證的實體副本。
雖然這是不推薦的,在嚴格串連限制操作下的使用者代理程式可能會違反該指令,如果是這樣,必須顯式警告使用者,提供了一個未經驗證的響應。該警告必須提供給每個未經驗證存取,並且應該要求顯式的使用者確認資訊。
proxy-revalidate
除了 proxy-revalidate 指令不能應用於非共用的使用者代理程式緩衝外,它與 must-revalidate 指令含義相同。proxy-revalidate 指令可以被用在響應一個已授權的請求,以便允許使用者緩衝儲存,之後返回無需重新驗證的響應(因為它已經被授權一次),但仍然需要代理來為使用者重新驗證(以確保每個使用者已授權)。
注意,這種已授權的響應也需要 public 緩衝控制指令,以便讓它們完全被緩衝。
No-Transform 指令
no-transform
中間緩衝(代理)的實施者已經發現它對轉換某個實體體的媒體類型很有用。例如,一個非透明的代理把映像轉換格式,以節省緩衝空間,或是減少慢速連結中的通訊量。
然而,當這些轉換被應用到實體體以便某種應用時,就會發生嚴重的操作性問題。例如,醫學成像、科學資料分析,以及點對點授權的應用程式來說,所有這些都依賴於接收的實體體,每個位元都要與原實體體一致。
因此,如果一個訊息包含 no-transform 指令,那麼中間緩衝或代理就不能改變頭(參看 RFC 2616 的 13.5.2 節 列出的)。這意味著,緩衝或代理不能改變由頭規定的實體體的任何方面,包括實體體自身的值。
緩衝控制擴充
Cache-Control 頭可以通過使用一個或多個 cache-extension 標記延伸,並為每個標記分配可選值。可以添加資訊擴充(不需要改變緩衝行為),而無需改變這些指令的語義。通過把現存的基本緩衝指令作為修飾符,設計行為擴充。同時提供新指令和標準指令,不能理解新指令的應用程式將預設採用標準指令的行為,可以理解新指令的那些程式將其與標準指令一起修改要求。通過這種方式,無需改變基本協議,就可以擴充 cache-control 指令。
該擴充機製取決於遵守其本地 HTTP 版本中定義的所有緩衝控制指令,以及一定程度的擴充,並忽略所有它不能理解的指令。
例如,假設一個新的稱為 "community" 的響應指令,它作為一個 private 指令的修飾符。我們定義這個新指令的含義是,除了任何非共用快取,只有由指定的 community 成員共用的任何緩衝可以緩衝響應。原始伺服器希望允許 UCI community 使用它們緩衝中的 private 響應,按如下方式
Cache-Control: private, community="UCI"
一個看到這個頭的緩衝將執行正確的行為,即使緩衝並不明白 community 緩衝擴充,因為它也將看到和理解 private 指令,因此去執行預設的安全行為。
無法識別的緩衝指令必須被忽略。它假定可能無法被 HTTP/1.1 緩衝識別的任何緩衝指令將被與標準指令(或響應預設的緩衝功能)結合,這樣,緩衝行為將保持最低限度的正確性,即使緩衝不能理解擴充。
參考資料
Wiki: Hypertext Transfer Protocol http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 http://tools.ietf.org/html/rfc2616
W3.org: A detailed technical history of HTTP http://www.w3.org/Protocols/History.html
W3.org: Design Issues by Berners-Lee when he was designing the protocol http://www.w3.org/Protocols/DesignIssues.html
Wiki: List of HTTP header fields http://en.wikipedia.org/wiki/List_of_HTTP_headers
httpwatch.com: HTTP Headers http://www.httpwatch.com/httpgallery/headers/
microsoft.com: HTTP Response Headers: http://msdn.microsoft.com/en-us/library/ms537417(v=VS.85).aspx
RFC 4229: HTTP Header Field Registrations http://tools.ietf.org/html/rfc4229
Internet Explorer and Custom HTTP Headers - EricLaw‘s IEInternals - Site Home - MSDN Blogs http://blogs.msdn.com/b/ieinternals/archive/2009/06/30/internet-explorer-custom-http-headers.aspx
HTTP Request Header Viewer http://www.myhttp.info/
HTTP Response Header Viewer - Retrieves the HTTP response headers of any domain http://viewdns.info/httpheaders/
HTTP Header with Privacyinfo - Display your HTTP request and response headers http://www.privacyinfo.org/http-headers
MSDN metal HTTP-EQUIV http://msdn.microsoft.com/en-us/library/ms533876(v=VS.85).aspx
HTTP 協議 Cache-Control 頭——效能啊