您當前的位置:首頁 > 詩詞

web效能最佳化之:no-cache與must-revalidate深入探究

作者:由 程式猿小卡 發表于 詩詞時間:2016-10-29

引言

稍微瞭解HTTP協議的前端同學,相比對Cache-Control不會感到陌生,效能最佳化時經常都會跟它打交道。

常見的值有有private、public、no-store、no-cache、must-revalidate、max-age等。

各個取值所代表的含義,網上總結挺多的,這裡就不打算再進行逐一介紹,感興趣的可以一起探討交流。

本文僅挑no-cache、must-revalidate 這兩個進行值進行探究對比。在專案實踐中,這兩個值用的比較多,也比較容易搞混。

Cache-Control: no-cache

Cache-Control: max-age=60, must-revalidate

傳送門:RFC2616關於Cache-Control首部的介紹。

如果對論證過程不感興趣,也可以直接跳到“

對比結論

”小節檢視結論。

no-cache、must-revalidate簡介

no-cache: 告訴瀏覽器、快取伺服器,不管本地副本是否過期,使用資源副本前,一定要到源伺服器進行副本有效性校驗。

must-revalidate:告訴瀏覽器、快取伺服器,本地副本過期前,可以使用本地副本;本地副本一旦過期,必須去源伺服器進行有效性校驗。

上面的介紹涉及三個主體:瀏覽器、快取伺服器、源伺服器。下面小節會簡單進行介紹。

瀏覽器、快取伺服器、源伺服器

瀏覽器:資源請求直接發起方。

源伺服器:資源實際提供方。

快取伺服器:在瀏覽器、源伺服器之間架設的中間伺服器,由它代替瀏覽器,向源伺服器發起資源請求;

快取伺服器作用如下。快取伺服器不是必須的,瀏覽器可也可與源伺服器直接通訊。

加速資源訪問速度,降低源伺服器的負載。快取伺服器從源伺服器獲取資源,並返回給瀏覽器。此外,快取伺服器一般還會在本地儲存資源的副本,當有相同的資源請求到來,快取伺服器可返回資源副本,以此提高資源訪問速度。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

對比測試場景、環境準備

對比測試場景

下文會透過以下兩種場景的對比測試,來探究no-cache、must-revalidate的區別。

瀏覽器 直接訪問 源伺服器。

瀏覽器 透過 快取伺服器,間接訪問 源伺服器。

環境準備

作業系統:OSX 10。11。4

瀏覽器:Chrome 52。0。2743。116 (64-bit)、Firefox 49。0。2

快取伺服器:Squid 3。6

源伺服器:Express 4。14。0

1、下載實驗程式碼:可以訪問github主頁獲取,也可透過git clone下載到本地。

git clone https://github。com/chyingp/tech-experiment。git

cd tech-experiment/2016。10。25-cache-control/

npm install

2、安裝Squid,步驟略,下載地址。

3、可選:啟動Squid,並將本地http代理設定為Squid的ip和埠。

備註:測試場景“透過快取伺服器,間接訪問源伺服器資源”時,才需要這一步。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

4、可選:將本地代理設定為Charles的地址,然後將Charles的代理地址設定為squid的代理地址。(避免瀏覽器開發者工具對request header的修改,干擾實驗結果)

場景一:瀏覽器->源伺服器

首先,透過以下指令碼啟動本地伺服器(源伺服器)。

cd connect-directly

node server。js

Cache-Control: no-cache

用例1:二次訪問,源伺服器 上 資源 未發生變化

訪問地址為:

http://

127。0。0。1:3000/no-cache

步驟一:第一次訪問,返回內容如下。可以看到,返回了Cache-Control: no-cache。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

HTTP/1。1 200 OK

X-Powered-By: Express

Cache-Control: no-cache

Content-Type: text/html; charset=utf-8

Content-Length: 11

ETag: W/“b-s0vwqaICscfrawwztfPIiA”

Date: Wed, 26 Oct 2016 07:46:28 GMT

Connection: keep-alive

步驟二:第二次訪問,返回內容如下。返回狀態碼為304 Not Modified,表示經過校驗,源伺服器上的資源沒有變化,瀏覽器可以採用本地副本。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

HTTP/1。1 304 Not Modified

X-Powered-By: Express

Cache-Control: no-cache

ETag: W/“b-s0vwqaICscfrawwztfPIiA”

Date: Wed, 26 Oct 2016 07:47:31 GMT

Connection: keep-alive

用例2:二次訪問,源伺服器 上 資源 發生變化

步驟一:訪問地址為:http://127。0。0。1:3000/no-cach。。。

備註:change=1告訴源伺服器,每次訪問都返回不同內容

步驟一:第一次訪問,內容如下,不贅述。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

HTTP/1。1 200 OK

X-Powered-By: Express

Cache-Control: no-cache

Content-Type: text/html; charset=utf-8

Content-Length: 11

ETag: W/“b-8n8r0vUN+mIIQCegzmqpuQ”

Date: Wed, 26 Oct 2016 07:48:01 GMT

Connection: keep-alive

步驟二:第二次訪問,返回內容如下。注意Etag變化了,表示源伺服器資源已發生變化。於是狀態碼為200 OK,源伺服器返回新版本的資源給瀏覽器。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

HTTP/1。1 200 OK

X-Powered-By: Express

Cache-Control: no-cache

Content-Type: text/html; charset=utf-8

Content-Length: 11

ETag: W/“b-0DK7Mx61dfZc1vIPJDSNSQ”

Date: Wed, 26 Oct 2016 07:48:38 GMT

Connection: keep-alive

Cache-Control: must-revalidate

訪問地址:http://127。0。0。1:3000/must-re。。。

可選引數說明:

max-age:源站返回的內容,max-age是多少(單位是s)。

change:源站返回的內容,是否變化,如果是1,則變化。

用例1:二次訪問,瀏覽器快取未過期

訪問地址:http://127。0。0。1:3000/must-re。。。

備註:max-age=10表示,希望資源快取10s

步驟一:第一次訪問,返回內容如下。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

HTTP/1。1 200 OK

X-Powered-By: Express

Cache-Control: max-age=10, must-revalidate

Content-Type: text/html; charset=utf-8

Content-Length: 16

ETag: W/“10-dK948plT5cojN3y7Cy717w”

Date: Wed, 26 Oct 2016 08:06:16 GMT

Connection: keep-alive

步驟二:第二次訪問(在10s內),如下截圖所示,瀏覽器直接從本地快取裡讀取資源副本,並沒有重新發起HTTP請求。

用例2:二次訪問,瀏覽器快取已過期,源伺服器 資源未變化

步驟一:第一次訪問略過。第二次訪問如下截圖所示(10s後),返回304 Not Modified。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

HTTP/1。1 304 Not Modified

X-Powered-By: Express

Cache-Control: max-age=10, must-revalidate

ETag: W/“10-dK948plT5cojN3y7Cy717w”

Date: Wed, 26 Oct 2016 08:09:22 GMT

Connection: keep-alive

用例3:瀏覽器快取已過期,源伺服器 資源 已變化

訪問地址:http://127。0。0。1:3000/must-re。。。

步驟一:第一次訪問,截圖如下。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

步驟二:第二次訪問(10s後),返回截圖如下,可以看到返回了200。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

場景2:瀏覽器->快取伺服器->源伺服器

從上面的對比實驗已經知道,在不經過快取伺服器的情況下,no-cache、must-revalidate在快取校驗方面的差別。

接下來,我們再看下,引入快取伺服器後,二者表現的差異點。

備註:下文我們會透過檢視Squid的訪問日誌,來確認快取伺服器的行為。這裡對日誌中的幾個關鍵字先粗略解釋下:

TCP_MISS:沒有命中快取。有可能是快取伺服器不存在資源的副本,也有可能資源副本已過期。

TCP_MEM_HIT:命中了快取。快取伺服器存在資源的副本,並且副本未過期。

再次貼上之前的圖。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

Cache-Control: no-cache

用例1:chrome第一次訪問資源

chrome訪問截圖如下:200 ok

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

squid日誌:TCP_MISS,表示沒有命中本地資源副本。

1477501799。573 17 127。0。0。1 TCP_MISS/200 299 GET http://127。0。0。1:3000/no-cache - HIER_DIRECT/127。0。0。1text/html

用例2:chrome再次訪問該資源。且源伺服器上,該資源未變化

訪問地址:

http://

127。0。0。1:3000/no-cache

第一次訪問略。第二次訪問,chrome訪問截圖如下:

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

squid訪問日誌如下:TCP_MISS/304 。表示快取伺服器 聯絡了 源伺服器,發現內容沒變化,於是返回304。

1477501987。785 1 127。0。0。1 TCP_MISS/304 238 GET http://127。0。0。1:3000/no-cache - HIER_DIRECT/127。0。0。1 -

用例3:chrome再次訪問該資源。且源伺服器上,該資源已變化

訪問地址:http://127。0。0。1:3000/no-cach。。。

備註:change=1 表示強制每次訪問源伺服器,返回的資源都是新的。

第一次訪問略。第二次訪問,chrome截圖如下,狀態碼為200。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

從squid日誌來看,快取伺服器 訪問 源伺服器,並返回200給瀏覽器。

1477647837。216 1 127。0。0。1 TCP_MISS/200 299 GET http://127。0。0。1:3000/no-cache? - HIER_DIRECT/127。0。0。1text/html

Cache-Control: must-revalidate

用例1:快取伺服器 已存在 資源副本,且該資源副本 未過期

訪問地址:http://127。0。0。1:3000/must-re。。。

備註:max-age=900表示資源有效期是900s

步驟一:

chrome第一次訪問 該資源,快取伺服器上沒有該資源副本,於是訪問源伺服器。最終,快取伺服器給瀏覽器返回200。此時,快取伺服器squid上有了資源的副本。

步驟二:

firefox第一次訪問 該資源(900s內)。快取伺服器上已有該資源副本,且該副本未過期。於是,快取伺服器給firefox返回該資源副本,且狀態碼為200。(快取命中)

為了驗證步驟二中,快取伺服器 返回的是本地資源的副本,檢視squid日誌。其中,第二條就是firefox的訪問記錄,TCP_MEM_HIT/200表示命中本地快取。

1477648947。594 5 127。0。0。1 TCP_MISS/200 325 GET http://127。0。0。1:3000/must-revalidate? - HIER_DIRECT/127。0。0。1 text/html

1477649012。625 0 127。0。0。1 TCP_MEM_HIT/200 333 GET http://127。0。0。1:3000/must-revalidate? - HIER_NONE/- text/html

用例2:快取伺服器 已存在 資源副本,該資源副本已過期,但源伺服器上 資源未改變

訪問連結:http://127。0。0。1:3000/must-re。。。

用chrome先後訪問該資源,其間間隔超過10s。第二次訪問時,chrome收到響應如下。

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

檢視squid日誌。可以看到,狀態為TCP_MISS/304,表示本地副本已過期,跟源伺服器進行校驗,發現源伺服器上資源未改變。於是,給瀏覽器返回304。

1477649429。105 11 127。0。0。1 TCP_MISS/304 258 GET http://127。0。0。1:3000/must-revalidate? - HIER_DIRECT/127。0。0。1-

用例3:快取伺服器 已存在 資源副本,該資源副本 已過期,但源伺服器上 資源已改變

訪問地址:http://127。0。0。1:3000/must-re。。。

用chrome先後訪問該資源,其間間隔超過10s。第二次訪問時,chrome收到響應如下

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

squid日誌如下,狀態都是TCP_MISS/200,表示沒有命中快取。

1477650702。807 8 127。0。0。1 TCP_MISS/200 325 GET http://127。0。0。1:3000/must-revalidate? - HIER_DIRECT/127。0。0。1 text/html

1477651020。516 4 127。0。0。1 TCP_MISS/200 325 GET http://127。0。0。1:3000/must-revalidate? - HIER_DIRECT/127。0。0。1 text/html

對比結論

以下針對的都是瀏覽器第n次訪問資源。(n>1)

不考慮快取伺服器

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

考慮快取伺服器

web效能最佳化之:no-cache與must-revalidate深入探究

web效能最佳化之:no-cache與must-revalidate深入探究

寫在後面

經過一輪對比測試,發現no-cache、must-revalidate這兩個值還是蠻有意思的。實際上,由於篇幅原因,這裡還有一些內容尚未進行對比實驗。比如:

當must-revalidate或no-cache跟max-stale一起使用時的表現。

no-cache跟max-age=0, mustvalidate的區別。

no-chche制定具體的欄位名時,跟不指明具體欄位名時,快取校驗行為上的區別。

proxy-revalidate跟must-revalidate的區別。

快取伺服器本身最佳化演算法對實驗結果的影響。

對比實驗過程比較枯燥繁瑣,如有不嚴謹或錯漏的地方,敬請指出 :)

這裡留個經常會碰到的問題,供讀者探討:no-cache跟max-age=0, mustvalidate的區別。

標簽: 伺服器  快取  127  訪問  NO