稍微了解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
:告诉浏览器、缓存服务器,本地副本过时前,可使用本地副本;本地副本一旦过时,必须去源服务器进行有效性校验。上面的介绍涉及三个主体:浏览器、缓存服务器、源服务器。下面小节会简单进行介绍。sql
缓存服务器做用以下。缓存服务器不是必须的,浏览器可也可与源服务器直接通讯。chrome
加速资源访问速度,下降源服务器的负载。缓存服务器从源服务器获取资源,并返回给浏览器。此外,缓存服务器通常还会在本地保存资源的副本,当有相同的资源请求到来,缓存服务器可返回资源副本,以此提升资源访问速度。shell
下文会经过如下两种场景的对比测试,来探究no-cache
、must-revalidate
的区别。npm
一、下载实验代码:能够访问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.1text/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.1text/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/...