引言

稍微了解HTTP协议的前端同学,相比对Cache-Control不会感到陌生,性能优化时经常都会跟它打交道。

常见的值有有privatepublicno-storeno-cachemust-revalidatemax-age等。

各个取值所代表的含义,网上总结挺多的,这里就不打算再进行逐一介绍,感兴趣的可以一起探讨交流。

本文仅挑no-cachemust-revalidate 这两个进行值进行探究对比。在项目实践中,这两个值用的比较多,也比较容易搞混。

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

传送门:RFC2616关于Cache-Control首部的介绍。 如果对论证过程不感兴趣,也可以直接跳到“对比结论”小节查看结论。

no-cache、must-revalidate简介

  • no-cache: 告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,一定要到源服务器进行副本有效性校验。
  • must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。

上面的介绍涉及三个主体:浏览器、缓存服务器、源服务器。下面小节会简单进行介绍。

浏览器、缓存服务器、源服务器

  • 浏览器:资源请求直接发起方。
  • 源服务器:资源实际提供方。
  • 缓存服务器:在浏览器、源服务器之间架设的中间服务器,由它代替浏览器,向源服务器发起资源请求;

缓存服务器作用如下。缓存服务器不是必须的,浏览器可也可与源服务器直接通信。

加速资源访问速度,降低源服务器的负载。缓存服务器从源服务器获取资源,并返回给浏览器。此外,缓存服务器一般还会在本地保存资源的副本,当有相同的资源请求到来,缓存服务器可返回资源副本,以此提高资源访问速度。

对比测试场景、环境准备

对比测试场景

下文会通过以下两种场景的对比测试,来探究no-cachemust-revalidate的区别。

  1. 浏览器 直接访问 源服务器。
  2. 浏览器 通过 缓存服务器,间接访问 源服务器。

环境准备

  • 操作系统: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和端口。

备注:测试场景“通过缓存服务器,间接访问源服务器资源”时,才需要这一步。

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

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-cache?change=1 备注: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

Cache-Control: must-revalidate

访问地址:http://127.0.0.1:3000/must-revalidate 可选参数说明:

  • max-age:源站返回的内容,max-age是多少(单位是s)。
  • change:源站返回的内容,是否变化,如果是1,则变化。

用例1:二次访问,浏览器缓存未过期

访问地址:http://127.0.0.1:3000/must-revalidate?max-age=10 备注: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-revalidate?max-age=10&change=1

步骤一:第一次访问,截图如下。

步骤二:第二次访问(10s后),返回截图如下,可以看到返回了200

场景2:浏览器->缓存服务器->源服务器

从上面的对比实验已经知道,在不经过缓存服务器的情况下,no-cachemust-revalidate在缓存校验方面的差别。

接下来,我们再看下,引入缓存服务器后,二者表现的差异点。

备注:下文我们会通过查看Squid的访问日志,来确认缓存服务器的行为。这里对日志中的几个关键字先粗略解释下:

  • TCP_MISS:没有命中缓存。有可能是缓存服务器不存在资源的副本,也有可能资源副本已过期。
  • TCP_MEM_HIT:命中了缓存。缓存服务器存在资源的副本,并且副本未过期。

再次贴上之前的图。

Cache-Control: no-cache

用例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-cache?change=1 备注: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

Cache-Control: must-revalidate

用例1:缓存服务器 已存在 资源副本,且该资源副本 未过期

访问地址:http://127.0.0.1:3000/must-revalidate?max-age=900 备注: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-revalidate?max-age=10

用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-revalidate?max-age=10&change=1

用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-cachemust-revalidate这两个值还是蛮有意思的。实际上,由于篇幅原因,这里还有一些内容尚未进行对比实验。比如:

  • must-revalidateno-cachemax-stale一起使用时的表现。
  • no-cachemax-age=0, mustvalidate的区别。
  • no-chche制定具体的字段名时,跟不指明具体字段名时,缓存校验行为上的区别。
  • proxy-revalidatemust-revalidate的区别。
  • 缓存服务器本身优化算法对实验结果的影响。

对比实验过程比较枯燥繁琐,如有不严谨或错漏的地方,敬请指出 :)

这里留个经常会碰到的问题,供读者探讨:no-cachemax-age=0, mustvalidate的区别。

相关链接

RFC2616 14.9: Cache-Control https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

web性能优化之:no-cache与must-revalidate深入探究的更多相关文章

  1. Web性能优化-合并js与css,减少请求

    Web性能优化已经是老生常谈的话题了, 不过笔者也一直没放在心上,主要的原因还是项目的用户量以及页面中的js,css文件就那几个,感觉没什么优化的.人总要进步的嘛,最近在被angularjs吸引着,也 ...

  2. Web性能优化 高并发网站解决 单例 已看1

    Web性能优化分为服务器端和浏览器端两个方面. 一.浏览器端,关于浏览器端优化,分很多个方面1.压缩源码和图片JavaScript文件源代码可以采用混淆压缩的方式,CSS文件源代码进行普通压缩,JPG ...

  3. web性能优化-浏览器渲染原理

    在web性能优化-浏览器工作原理中讲到,浏览器渲染是在renderer process中完成的. 那我们来看下renderer process究竟干了什么? Renderer Process包含的线程 ...

  4. 关于WEB 性能优化 (摘抄)

    压缩源代码和图片 JavaScript文件源代码可以采用混淆压缩的方式,CSS文件源代码进行普通压缩,JPG图片可以根据具体质量来压缩为50%到70%,PNG可以使用一些开源压缩软件来压缩,比如24色 ...

  5. web性能优化——浏览器相关

    简介 优化是一个持续的过程.所以尽可能的不要有人为的参与.所以能自动化的或者能从架构.框架级别解决的就最更高级别解决. 这样即能实现面对开发人员是透明的.不响应,又能确保所有资源都是被优化过的. 场景 ...

  6. Web性能优化系列

    web性能优化之重要,这里并不打算赘述.本系列课程将带领大家认识.熟悉.深刻体会并且懂得如果去为不同的站点做性能优化 同时,本系列将还会穿插浏览器兼容性相关问题的解决方案,因为在我看来,兼容性同样属于 ...

  7. 移动web性能优化笔记

    移动web性能优化 最近看了一些文章,对移动web性能优化方法,做一个简单笔记 笔记内容主要出自 移动H5前端性能优化指南和移动前端系列——移动页面性能优化

  8. web性能优化 来自《web全栈工程师的自我修养》

    最近在看<web全栈工程师的自我修养>一书,作者是来自腾讯的前端工程师.作者在做招聘前端的时候问应聘者web新能优化有什么了解和经验,应聘者思索后回答“在发布项目之前压缩css和 Java ...

  9. web性能优化之--合理使用http缓存和localStorage做资源缓存

    一.前言 开始先扯点别的: 估计很多前端er的同学应该遇到过:在旧项目中添加新的功能模块.或者修改一些静态文件时候,当代码部署到线上之后,需求方验收OK,此时你送了一口气,当你准备开始得意于自己的ma ...

  10. Web 性能优化:Preload与Prefetch的使用及在 Chrome 中的优先级

    摘要: 理解Preload与Prefetch. 原文:Web 性能优化:Preload,Prefetch的使用及在 Chrome 中的优先级 作者:前端小智 Fundebug经授权转载,版权归原作者所 ...

随机推荐

  1. 搭建Struts2不同版本jar包不同

    struts2的版本比较多,所以在开发的时候特别要注意版本不同所需引入的包是不一样的.否则,会出现各种问题.而且很难找到问题所在. 以下是我遇到的问题总结: 一.当我运用struts2.3.4.1时, ...

  2. Linux iptables

    一.简介 http://liaoph.com/iptables/ 二.操作 1)查看规则 iptables -t filter -L -n iptables -t nat -L -n iptables ...

  3. php类型转换以及类型转换的判别

    部分摘自PHP: 类型 - Manual 相关链接 PHP 在变量定义中不需要(或不支持)明确的类型定义:变量类型是根据使用该变量的上下文所决定的.也就是说,如果把一个 string 值赋给变量 $v ...

  4. 浅谈export 以及环境变量

    简要说一下env,set,export的区别:env命令显示环境变量,set和export显示环境变量和自定变量. export:可以讲自定变量转化为环境变量之前有一个疑惑,我们定义环境变量PATH时 ...

  5. iTOP-4412开发板低功耗高性能的开源硬件平台——上手评测

    iTOP-4412开发板现在比较热门的开发板,笔者近期入了一套.也推荐给初学ARM的朋友学习,4412开发板搭载三星Exynos四核处理器,配备1GB内存,4GB固态硬盘EMMC存储,兼具快速读取与超 ...

  6. uva 1152 4 values whose sum is zero ——yhx

    The SUM problem can be formulated as follows: given four lists A;B;C;D of integer values, computehow ...

  7. uva 120 stacks of flapjacks ——yhx

     Stacks of Flapjacks  Background Stacks and Queues are often considered the bread and butter of data ...

  8. 【读书笔记《Android游戏编程之从零开始》】3.Android 游戏开发常用的系统控件(Button、Layout、ImageButton)

    3.1 Button Button这控件不用多说,就是一个按钮,主要是点击后进行相应事件的响应. 给组件添加ID属性:定义格式为 android:id="@+id/name",这里 ...

  9. 图论 公约数 找环和链 BZOJ [NOI2008 假面舞会]

    BZOJ 1064: [Noi2008]假面舞会 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1655  Solved: 798[Submit][S ...

  10. 简单的抓取淘宝关键字信息、图片的Python爬虫|Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具(第一篇)

    Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具(第一篇) 淘宝改字段,Bugfix,查看https://github.com/hunterhug/taobaoscrapy.git 由于Gith ...