原文地址:http://hi.baidu.com/feilala_fly/item/f79eca08fbf389026c9048a7html
Http的Cache機制總共有4個組成部分:前端
Cache-Control、Last-Modified(If-Modified-Since)、Etag(If-None-Match) 、Expiresnode
服務器響應頭:Last-Modified,Etag
瀏覽器請求頭:If-Modified-Since,If-None-Match算法
服務器發出Etag,Last-Modified頭後,下次瀏覽器再進行一樣的請求,則會發出If-None-Match,If-apache
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。
基礎知識
1) 什麼是」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會認爲是個非法請求
2) 什麼是」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標記,服務器可在稍後使用它來判斷頁面是否已經被修改。本質上,客戶端經過將該記號傳回服務器要求服務器驗證其(客戶端)緩存。
過程以下:
1. 客戶端請求一個頁面(A)。
2. 服務器返回頁面A,並在給A加上一個Last-Modified/ETag。
3. 客戶端展示該頁面,並將頁面連同Last-Modified/ETag一塊兒緩存。
4. 客戶再次請求頁面A,並將上次請求時服務器返回的Last-Modified/ETag一塊兒傳遞給服務器。
5. 服務器檢查該Last-Modified或ETag,並判斷出該頁面自上次客戶端請求以後還未被修改,直接返回響應304和一個空的響應體。
注:
一、Last-Modified和Etag頭都是由Web Server發出的Http Reponse Header,Web Server應該同時支持這兩種頭。
二、Web Server發送完Last-Modified/Etag頭給客戶端後,客戶端會緩存這些頭;
三、客戶端再次發起相同頁面的請求時,將分別發送與Last-Modified/Etag對應的Http Request Header:If-Modified-Since和If-None-Match。咱們能夠看到這兩個Header的值和Web Server發出的Last-Modified,Etag值徹底同樣;
四、經過上述值到服務器端檢查,判斷文件是否繼續緩存;
關於Etag和Last-Modified網上還有更精闢的解釋
一、關於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比一個靜態資源一般小多了,這樣就節省了網絡帶寬。
二、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一塊兒用。
三、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無法作到的。
四、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中加一行就搞定了:
五、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的配置。