原文鏈接:http://tech110.blog.51cto.com/438717/549764
Http的Cache機制總共有4個組成部分:
Cache-Control、Last-Modified(If-Modified-Since)、Etag(If-None-Match) 、Expires
服務器響應頭:Last-Modified,Etag瀏覽器請求頭:If-Modified-Since,If-None-Match
服務器發出Etag,Last-Modified頭后,下次瀏覽器再進行同樣的請求,則會發出If-None-Match,If-
Modified-Since頭,而后服務器根據這些信息來判斷是否需要發送數據,如果沒有更新,服務器就簡單的
發送一個304狀態告訴瀏覽器用緩存就OK了,不用下載數據了,從而節約了帶寬。
Last-Modified / If-Modified-Since
Last-Modified是響應頭,If-Modified-Since是請求頭。Last-Modified把Web組件的最后修改時間告訴客
戶端,客戶端在下次請求此Web組件的時候,會把上次服務端響應的最后修改時間作為If-Modified-Since
的值發送給服務器,服務器可以通過這個值來判斷是否需要重新發送,如果不需要,就簡單的發送一個
304狀態碼,客戶端將從緩存里直接讀取所需的Web組件。如果有更新,返回HTTP 200和更新的頁面內容,
并且攜帶新的”ETag”和”LastModified”。
使用這個機制,能夠避免重復發送文件給瀏覽器,不過仍然會產生一個HTTP請求。
ETag / If-None-Match
ETag是響應頭,If-None-Match是請求頭。Last-Modified / If-Modified-Since的主要缺點就是它只能精確到秒的級別,一旦在一秒的時間里出現了多次修改,那么Last-Modified / If-Modified-Since是無法體現的。相比較,ETag / If-None-Match沒有使用時間作為判斷標準,而是使用一個特征串。Etag把Web組件的特征串告訴客戶端,客戶端在下次請求此Web組件的時候,會把上次服務端響應的特征串作為If-None-Match的值發送給服務端,服務端可以通過這個值來判斷是否需要從重新發送,如果不需要,就簡單的發送一個304狀態碼,客戶端將從緩存里直接讀取所需的Web組件。因此,HTTP/1.1利用Entity Tag頭提供了更加嚴格的驗證。
當服務器發出響應的時候,可以通過兩種方式來告訴客戶端緩存請求:
第一種是Expires,比如:Expires: Sun, 16 Oct 2016 05:43:02 GMT在此日期之前,客戶端都會認為緩存是有效的。
不過Expires有缺點,比如說,服務端和客戶端的時間設置可能不同,這就會使緩存的失效可能并不能精確的按服務器的預期進行。
第二種是Cache-Control,比如:Cache-Control: max-age=3600
這里聲明的是一個相對的秒數,表示從現在起,3600秒內緩存都是有效的,這樣就避免了服務端和客戶端時間不一致的問題。
但是Cache-Control是HTTP1.1才有的,不適用與HTTP1.0,而Expires既適用于HTTP1.0,也適用于HTTP1.1,所以說在大多數情況下同時發送這兩個頭會是一個更好的選擇,當客戶端兩種頭都能解析的時候,會優先使用Cache-Control基礎知識
什么是”Last-Modified”?
在瀏覽器第一次請求某一個URL時,服務器端的返回狀態會是200,內容是你請求的資源,同時有一個Last-Modified的屬性標記(Http Reponse Header)此文件在服務期端最后被修改的時間,格式類似這樣:
Last-Modified: Fri, 12 May 2006 18:53:33 GMT
客戶端第二次請求此URL時,根據 HTTP 協議的規定,瀏覽器會向服務器傳送 If-Modified-Since 報頭(Http Request Header),詢問該時間之后文件是否有被修改過:
If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
如果服務器端的資源沒有變化,則自動返回 HTTP 304 (Not Changed.)狀態碼,內容為空,這樣就節省了傳輸數據量。當服務器端代碼發生改變或者重啟服務器時,則重新發出資源,返回和第一次請求時類似。從而保證不向客戶端重復發出資源,也保證當服務器有變化時,客戶端能夠得到最新的資源。 注:如果If-Modified-Since的時間比服務器當前時間(當前的請求時間request_time)還晚,Apache會認為是個非法請求什么是”Etag”?
HTTP 協議規格說明定義ETag為“被請求變量的實體值” (參見 —— 章節 14.19)。 另一種說法是,ETag是一個可以與Web資源關聯的記號(token)。典型的Web資源可以一個Web頁,但也可能是JSON或XML文檔。服務器單獨負責判斷記號是什么及其含義,并在HTTP響應頭中將其傳送到客戶端,以下是服務器端返回的格式:
ETag: "50b1c1d4f775c61:df3"
客戶端的查詢更新格式是這樣的:
If-None-Match: "50b1c1d4f775c61:df3"
如果ETag沒改變,則返回狀態304然后不返回,這也和Last-Modified一樣。本人測試Etag主要在斷點下載時比較有用。
Last-Modified和Etags如何幫助提高性能?
聰明的開發者會把Last-Modified 和ETags請求的http報頭一起使用,這樣可利用客戶端(例如瀏覽器)的緩存。因為服務器首先產生 Last-Modified/Etag標記,服務器可在稍后使用它來判斷頁面是否已經被修改。本質上,客戶端通過將該記號傳回服務器要求服務器驗證其(客戶端)緩存。
過程如下:
- 客戶端請求一個頁面(A)。
- 服務器返回頁面A,并在給A加上一個Last-Modified/ETag。
- 客戶端展現該頁面,并將頁面連同Last-Modified/ETag一起緩存。
- 客戶再次請求頁面A,并將上次請求時服務器返回的Last-Modified/ETag一起傳遞給服務器。
- 服務器檢查該Last-Modified或ETag,并判斷出該頁面自上次客戶端請求之后還未被修改,直接返回響應304和一個空的響應體。
注:
1、Last-Modified和Etag頭都是由Web Server發出的Http Reponse Header,Web Server應該同時支持這兩種頭。
2、Web Server發送完Last-Modified/Etag頭給客戶端后,客戶端會緩存這些頭;
3、客戶端再次發起相同頁面的請求時,將分別發送與Last-Modified/Etag對應的Http Request Header:If-Modified-Since和If-None-Match。我們可以看到這兩個Header的值和Web Server發出的Last-Modified,Etag值完全一樣;
4、通過上述值到服務器端檢查,判斷文件是否繼續緩存;
關于Etag和Last-Modified網上還有更精辟的解釋1、關于Last-Modified
HTTP的Response中還會有另外一個Header叫Last-Modified,比如Last-Modified: Thu, 06 Apr 2006 21:17:12 GMT
,瀏覽器訪問一個URI得到這樣的Resposne之后,就知道這個資源最后一次的修改時間,下次需要再次獲得這個資源的時候,會發一個Request給Server,不過這個Request中有一條If-Unmodified-Since: Thu, 06 Apr 2006 21:17:12 GMT
,如果在Server端在這個日期之后對這個資源進行了修改,就會照常返回這個資源給Client端,但是如果沒有修改,就會返回一個304 (Not Modified) Response而不返回資源,告訴Client端:“這個資源從上次給你之來從來沒改過,你放心用你Cache中的好了。” 一個304 Response比一個靜態資源通常小多了,這樣就節省了網絡帶寬。
2、Last-Modified和Expires的區別
讓我們回過頭來比較一下Expires和Last-Modified這兩個東西,似乎Last-Modified比不上Expires,因為雖然它能夠節省一點帶寬,但是還是逃不掉發一個HTTP請求出去,而Expires卻使得瀏覽器干脆連HTTP請求都不用發,豈不痛快!那還要Last- Modified這個物體干什么?理想狀況的確是這樣,不過當用戶在IE或者Firefox里面按F5或者點擊Refresh按鈕的時候(不是在URL欄里重新輸入一遍URL然后回車),就算對于有Expires的URI,一樣也會發一個HTTP請求出去,所以,Last-Modified還是要用的,而且要和Expires一起用。
3、Etag
除了Last-Modified,HTTP Response中還可能有另外一個Header: ETag,使得Server上的靜態資源有點“版本控制”的味道,假如HTTP Response中包含ETag: "abcdefg1234:0001"等于告訴Client端,你拿到的這個版本的資源有個ID,叫做abcdefg1234:0001,下次需要發Request索要同一個URI的時候,在Request里面加一條If-None-Match: "abcdefg1234:0001"好,Server 端做了一些修改,下次這個Client再來了一個請求,但是這時候資源已經改了,所以返回這個新資源,還有新的tag “ETag: "abcdefg4567:0001"”(這個etag我是胡寫的),這樣,Client端等于Cache了兩份,在需要索要這個資源的時候,可以包含這樣的Header: “If-None-Match: "abcdefg1234:0001" "abcdefg4567:0001"”,這樣,即使Server端頭腦發熱,把這個資源Roll back回原來的版本,依然會返回304 (Not Modified) Response,因為它知道Client端Cache著以前的版本呢,這點功能是Last-Modifed/If-Not-Modified沒法做到的。
4、Etag的弊端
不過ETag/If-None-Match這點功能實在是個雞肋,首先,Server端的資源不大可能Roll Back,更重要的是,有可能造成Client Performance下降。對于只有一個Server的網站,沒什么問題,但是現在稍微上點規模的網站都需要Scale Out,也就是說需要前端一個Load Balancer,后面接多臺Server來處理請求,俗稱Cluster,既然是Cluster,那么每個請求到底返回什么結果應該和分配到哪個 Server無關,不過這個ETag可能就壞事了。假如用戶的第一次請求分配給Server A,返回“ETag: "abcdefg1234:0001"”,但是第二次請求分配給了Server B,Server B上這個資源和Server A上的一模一樣,但是計算出這個資源的ETag是"abcdefg1234:0002",這下麻煩了,雖然內容一樣,但是ETag不匹配,還是浪費了帶寬把資源發送了一遍,冤枉啊!而事實上,不同Server上的ETag很有可能不同,對于Apache,ETag的計算考慮了inode,對于 IIS,ETag考慮了metabase的修改版本,要保證不同server上的這些信息一致,有點小難。不過不是有Last-Modified/If- Not-Modified嗎?Server端看到If-Modified-Since,對照一下時間對得上,不管If-None-Match,可以直接發回304(Not Modified)呀,很不幸,RFC2616對這種情況做了規定,如果既有If-None-Match又有If-Modified-Since,除非兩者不沖突,不然不會返回304。
所以說ETag就是一個害人精,按照Yahoo的建議,別費勁想辦法同步不同Server上的ETag了,干脆就把ETag刪除得了(缺省,Apache和 IIS都是有ETag的),我Sniff了一下Yahoo的若干網頁返回HTTP Response,的確沒有ETag,人家的確是知行合一。
對于Apache,在httpd.conf或者.htaccess中加一行就搞定了:
5、Apache中的Etag設置補充:Apache默認開啟Etag,可以使用FileEtag來設置FileETag none|INode|MTime|Size|All從apache的實現中http_etag.c我們可以發現,Apache的Etag包括了Inode|Mtime|Size這些因素。
對于IIS 6,可就有點費勁了,首先,似乎沒有辦法通過Config來把ETag去掉,查了很多資料,問了很多人,似乎能夠去掉ETag的辦法只有寫一個ISAPI Filter來弄,Sniff了一下Microsoft的幾個網頁的結果顯示ETag都穩當當的存在,估計目前真的沒有什么好方法。
只好退而取其次,保證不同Server上的ETag一致了。 IIS對Etag的計算算法是ETag = {Filetimestamp:ChangeNumber}, Filetimestamp保持一致沒什么問題,ChangeNumber是metabase的change number,就有點難保證Cluster中每個Server都一樣了,所以,干脆就把它設成固定值好了,這個連接告訴我們該怎么辦,很可惜,沒有找到徹底刪除ETags的配置。
當然轉載于此方便以后自己查閱!!!!