稍微瞭解HTTP協議的前端同窗,想必對Cache-Control
不會感到陌生,性能優化時常常都會跟它打交道。html
常見的值有有private
、public
、no-store
、no-cache
、must-revalidate
、max-age
等。前端
各個取值所表明的含義,網上總結挺多的,這裏就不打算再進行逐一介紹,感興趣的能夠一塊兒探討交流。node
本文僅挑no-cache
、must-revalidate
這兩個進行值進行探究對比。在項目實踐中,這兩個值用的比較多,也比較容易搞混。git
Cache-Control: no-cache
Cache-Control: max-age=60, must-revalidategithub
傳送門:RFC2616關於Cache-Control首部的介紹。算法
no-cache
: 告訴瀏覽器、緩存服務器,無論本地副本是否過時,使用資源副本前,必定要到源服務器進行副本有效性校驗。must-revalidate
:告訴瀏覽器、緩存服務器,本地副本過時前,可使用本地副本;本地副本一旦過時,必須去源服務器進行有效性校驗。上面的介紹涉及三個主體:瀏覽器、緩存服務器、源服務器。下面小節會簡單進行介紹。chrome
緩存服務器做用以下。緩存服務器不是必須的,瀏覽器可也可與源服務器直接通訊。shell
加速資源訪問速度,下降源服務器的負載。緩存服務器從源服務器獲取資源,並返回給瀏覽器。此外,緩存服務器通常還會在本地保存資源的副本,當有相同的資源請求到來,緩存服務器可返回資源副本,以此提升資源訪問速度。npm
下文會經過如下兩種場景的對比測試,來探究no-cache
、must-revalidate
的區別。瀏覽器
一、下載實驗代碼:能夠訪問github主頁獲取,也可經過git clone
下載到本地。
git clone https://github.com/chyingp/tech-experiment.git cd tech-experiment/2016.10.25-cache-control/ npm install
二、安裝Squid,步驟略,下載地址。
三、可選:啓動Squid,並將本地http代理設置爲Squid的ip和端口。
備註:測試場景「經過緩存服務器,間接訪問源服務器資源」時,才須要這一步。
四、可選:將本地代理設置爲Charles的地址,而後將Charles的代理地址設置爲squid的代理地址。(避免瀏覽器開發者工具對request header的修改,干擾實驗結果)
首先,經過如下腳本啓動本地服務器(源服務器)。
cd connect-directly node server.js
用例1:二次訪問,源服務器 上 資源 未發生變化
訪問地址爲:http://127.0.0.1:3000/no-cache
步驟一:第一次訪問,返回內容以下。能夠看到,返回了Cache-Control: no-cache
。
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
,表示通過校驗,源服務器上的資源沒有變化,瀏覽器能夠採用本地副本。
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
告訴源服務器,每次訪問都返回不一樣內容
步驟一:第一次訪問,內容以下,不贅述。
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
,源服務器返回新版本的資源給瀏覽器。
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
訪問地址: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
步驟一:第一次訪問,返回內容以下。
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
。
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...
步驟一:第一次訪問,截圖以下。
步驟二:第二次訪問(10s後),返回截圖以下,能夠看到返回了200
。
從上面的對比實驗已經知道,在不通過緩存服務器的狀況下,no-cache
、must-revalidate
在緩存校驗方面的差異。
接下來,咱們再看下,引入緩存服務器後,兩者表現的差別點。
備註:下文咱們會經過查看Squid
的訪問日誌,來確認緩存服務器的行爲。這裏對日誌中的幾個關鍵字先粗略解釋下:
再次貼上以前的圖。
用例1:chrome第一次訪問資源
chrome訪問截圖以下:200 ok
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.1 text/html
用例2:chrome再次訪問該資源。且源服務器上,該資源未變化
訪問地址:http://127.0.0.1:3000/no-cache
第一次訪問略。第二次訪問,chrome訪問截圖以下:
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
。
從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.1 text/html
用例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收到響應以下。
查看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收到響應以下
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)
首部 | 本地緩存是否過時 | 源服務器資源是否改變 | 是否從新校驗 | 狀態碼 |
---|---|---|---|---|
no-cache | 不肯定 | 否 | 是 | 304 |
no-cache | 不肯定 | 是 | 是 | 200 |
must-revalidate | 否 | 是/否 | 否 | 200(來自瀏覽器緩存) |
must-revalidate | 是 | 否 | 是 | 304 |
must-revalidate | 是 | 是 | 是 | 200 |
首部 | 本地緩存是否過時 | 緩存服務器副本是否過時 | 源服務器資源是否改變 | 是否從新校驗 | 狀態碼 |
---|---|---|---|---|---|
no-cache | 不肯定 | 不肯定 | 否 | 是 | 304 |
no-cache | 不肯定 | 不肯定 | 是 | 是 | 200 |
must-revalidate | 否 | 是/否 | 是/否 | 否 | 200(來自瀏覽器緩存) |
must-revalidate | 是 | 否 | 是/否 | 是 | 304(來自緩存服務器) |
must-revalidate | 是 | 是 | 否 | 是 | 304 |
must-revalidate | 是 | 是 | 是 | 是 | 200 |
通過一輪對比測試,發現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
的區別。
RFC2616 14.9: Cache-Control
https://www.w3.org/Protocols/...