快取(cache)是計算機效能的乙個重要概念。
由於鏈路漫長,網路時延不可控,瀏覽器使用 http 獲取資源的成本較高。所以,非常有必要把「來之不易」的資料快取起來,下次再請求的時候盡可能地復用。
一、伺服器的快取控制(伺服器負責控制,瀏覽器負責快取)
我們以生鮮速遞作為例子說一下。
如果我現在想要吃乙個西瓜,開啟冰箱發現冰箱是空的,那我要做的就是在生鮮超市上下單,生鮮超市配送到我手裡,上面提示西瓜有效時間是5天,然後我吃了一部分西瓜後放到冰箱裡頭,等我想吃的時候再拿出來吃。
這裡的生鮮超市就是指web伺服器,我就是瀏覽器角色,冰箱就是瀏覽器記憶體角色,整個流程翻譯成http就是:
1. 瀏覽器發起請求,查詢瀏覽器快取,沒有就訪問到web伺服器
2. web伺服器將帶有資源有限時間的資源返給瀏覽器
3. 瀏覽器快取資源,等待下次重用(資源有效時間是5天)
訪問實驗環境的 uri 「/20-1」,看看具體的請求 - 應答過程。
伺服器標記資源有效期使用的頭字段是「cache-control」,裡面的值「max-age=30」就是資源的有效時間,相當於告訴瀏覽器,「這個頁面只能快取 30 秒,之後就算是過期,不能用。」就像西瓜一樣,要給乙個有效時間,因為過期後水果就壞了,資源也是一樣,資源有時候回存在更新情況,如果一直都從快取中拿舊資料,那肯定就不行了。
cache-control有如下幾個屬性:
2. no_store:表示資源不可快取
3. no_cache:表示資源可以快取,但是使用前要去伺服器驗證資源是否過期,是否還有更新版本出現
4. must-revalidate:表示資源快取沒過期就能繼續用,過期後如果還想要的話就要去伺服器進行驗證。
現在我們還是用生鮮超市買西瓜作為例子學習:
1. max-age = 5 :5天內吃完水果
2. no_store:水果從超市雲回來後立刻吃完,不能存放。
3. no_cache:水果從超市買回來後可以存放,但是吃水果前要去超市問一下是否還有更新鮮的水果,如果有的話就扔掉當前水果買最新的。
4. must-revalidate:水果從超市買回來後可以存放,沒過期前可以正常使用,過期後就要詢問超市是否可以使用。
二、客戶端的快取控制
你可以在 chrome 裡點幾次「重新整理」按鈕,估計你會失望,頁面上的 id 一直在變,根本不是快取的結果,明明說快取 30 秒,怎麼就不起作用呢?
其實不止伺服器可以發「cache-control」頭,瀏覽器也可以發「cache-control」,也就是說請求 - 應答的雙方都可以用這個字段進行快取控制,互相協商快取的使用策略。
當你點「重新整理」按鈕的時候,瀏覽器會在請求頭裡加乙個「cache-control: max-age=0」。因為 max-age 是「生存時間」,max-age=0 的意思就是「我要乙個最最新鮮的西瓜」,而本地快取裡的資料至少儲存了幾秒鐘,所以瀏覽器就不會使用快取,而是向伺服器發請求。伺服器看到 max-age=0,也就會用乙個最新生成的報文回應瀏覽器。
我們來看一下頁面重新整理請求時請求頭中內容:
這裡我們驚奇的發現,這裡的資料是從瀏覽器快取中拿出來的,這是因為請求頭中並沒有cache-control欄位,所以瀏覽器只能怪怪的回到瀏覽器快取中查詢內容,查詢到了就返回資料了。
三、條件請求
瀏覽器用「cache-control」做快取控制只能是重新整理資料,不能很好地利用快取資料,又因為快取會失效,使用前還必須要去伺服器驗證是否是最新版。
瀏覽器可以用兩個連續的請求組成「驗證動作」:先是乙個 head,獲取資源的修改時間等元資訊,然後與快取資料比較,如果沒有改動就使用快取,節省網路流量,否則就再發乙個 get 請求,獲取最新的版本。
但這樣的兩個請求網路成本太高了,所以 http 協議就定義了一系列「if」開頭的「條件請求」字段,專門用來檢查驗證資源是否過期,把兩個請求才能完成的工作合併在乙個請求裡做。而且,驗證的責任也交給伺服器,瀏覽器只需「坐享其成」。
條件請求一共有 5 個頭字段,我們最常用的是「if-modified-since」和「if-none-match」這兩個。需要第一次的響應報文預先提供「last-modified」和「etag」,然後第二次請求時就可以帶上快取裡的原值,驗證資源是否是最新的。
如果資源沒有變,伺服器就回應乙個「304 not modified」,表示快取依然有效,瀏覽器就可以更新一下有效期,然後放心大膽地使用快取了。
etag 是「實體標籤」(entity tag)的縮寫,是資源的乙個唯一標識,主要是用來解決修改時間無法準確區分檔案變化的問題。
比如,乙個檔案在一秒內修改了多次,但因為修改時間是秒級,所以這一秒內的新版本無法區分。
再比如,乙個檔案定期更新,但有時會是同樣的內容,實際上沒有變化,用修改時間就會誤以為發生了變化,傳送給瀏覽器就會浪費頻寬。
使用 etag 就可以精確地識別資源的變動情況,讓瀏覽器能夠更有效地利用快取。etag 還有「強」「弱」之分。
強 etag 要求資源在位元組級別必須完全相符,弱 etag 在值前有個「w/」標記,只要求資源在語義上沒有變化,但內部可能會有部分發生了改變(例如 html 裡的標籤順序調整,或者多了幾個空格)。
還是拿生鮮速遞做比喻最容易理解:
你打**給超市,「我這個西瓜是 3 天前買的,還有最新的嗎?」。超市看了一下庫存,說:「沒有啊,我這裡都是 3 天前的。」於是你就知道了,再讓超市送貨也沒用,還是吃冰箱裡的西瓜吧。這就是「if-modified-since」和「last-modified」。
第三次打**,你說「有不是 8 斤的沙瓤西瓜嗎?」,這回超市給了你滿意的答覆:「有個 10 斤的沙瓤西瓜」。於是,你就扔掉了冰箱裡的存貨,讓超市重新送了乙個新的大西瓜。這就是「if-none-match」和「強 etag」。
再來看看實驗環境的 uri 「/20-2」。它為資源增加了 etag 字段,重新整理頁面時瀏覽器就會同時傳送快取控制頭「max-age=0」和條件請求頭「if-none-match」,如果快取有效伺服器就會返回304:
四、課後習題
1. cache 和 cookie 都是伺服器發給客戶端並儲存的資料,你能比較一下兩者的異同嗎?
答:cache 和 cookie 的相同點是:都會儲存到瀏覽器中,並可以設定過期時間。 cookie 會隨請求報文傳送到伺服器,而 cache 不會,但可能會攜帶 if-modified-since(儲存資源的最後修改時間)和 if-none-match(儲存資源唯一標識) 欄位來驗證資源是否過期。
2. 即使有「last-modified」和「etag」,強制重新整理(ctrl+f5)也能夠從伺服器獲取最新資料(返回 200 而不是 304),請你在實驗環境裡試一下,觀察請求頭和響應頭,解釋原因。
答:發現強制重新整理後請求頭中 沒有了 if-none-match ,以及 cache-control欄位也變了,變成了 cache-control: no-cache
至此,結束。
HTTP的快取控制
1.快取的分類 1 快取分為服務端側 server side,比如 nginx apache 和客戶端側 client side,比如 web browser 2 服務端快取又分為 伺服器快取 和 反向 伺服器快取 也叫閘道器快取,比如 nginx反向 squid等 其實廣泛使用的 cdn 也是一種...
http學習 http的快取控制
由於請求 應答模式的通訊成本比較高,所以有必要將某些資料進行快取,從而節省頻寬。快取是優化系統效能的重要手段,http 傳輸的每乙個環節中都可以有快取 2.1 快取控制的流程 瀏覽器發現快取無資料,於是傳送請求,向伺服器獲取資源 伺服器響應請求,返回資源,同時標記資源的有效期 瀏覽器快取資源,等待下...
http頭部如何對快取的控制
文章自于我的個人部落格 使用快取的目的就是在於減少計算,io,網路等時間,可以快速的返回,特別是流量比較大的時候,可以節約很多伺服器頻寬和壓力。乙個請求從快取的方面來說,有三個過程。expires,http 1.0版本定義的response頭部,定義過期時間,如果本地時間發現超過過期時間,就會向伺服...