web性能优化之:no-cache与must-revalidate深入探究
引言
稍微了解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
:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。
上面的介绍涉及三个主体:浏览器、缓存服务器、源服务器。下面小节会简单进行介绍。
浏览器、缓存服务器、源服务器
- 浏览器:资源请求直接发起方。
- 源服务器:资源实际提供方。
- 缓存服务器:在浏览器、源服务器之间架设的中间服务器,由它代替浏览器,向源服务器发起资源请求;
缓存服务器作用如下。缓存服务器不是必须的,浏览器可也可与源服务器直接通信。
加速资源访问速度,降低源服务器的负载。缓存服务器从源服务器获取资源,并返回给浏览器。此外,缓存服务器一般还会在本地保存资源的副本,当有相同的资源请求到来,缓存服务器可返回资源副本,以此提高资源访问速度。
对比测试场景、环境准备
对比测试场景
下文会通过以下两种场景的对比测试,来探究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和端口。
备注:测试场景“通过缓存服务器,间接访问源服务器资源”时,才需要这一步。
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-cache
、must-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-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/rfc2616/rfc2616-sec14.html#sec14.9
web性能优化之:no-cache与must-revalidate深入探究的更多相关文章
- Web性能优化-合并js与css,减少请求
Web性能优化已经是老生常谈的话题了, 不过笔者也一直没放在心上,主要的原因还是项目的用户量以及页面中的js,css文件就那几个,感觉没什么优化的.人总要进步的嘛,最近在被angularjs吸引着,也 ...
- Web性能优化 高并发网站解决 单例 已看1
Web性能优化分为服务器端和浏览器端两个方面. 一.浏览器端,关于浏览器端优化,分很多个方面1.压缩源码和图片JavaScript文件源代码可以采用混淆压缩的方式,CSS文件源代码进行普通压缩,JPG ...
- web性能优化-浏览器渲染原理
在web性能优化-浏览器工作原理中讲到,浏览器渲染是在renderer process中完成的. 那我们来看下renderer process究竟干了什么? Renderer Process包含的线程 ...
- 关于WEB 性能优化 (摘抄)
压缩源代码和图片 JavaScript文件源代码可以采用混淆压缩的方式,CSS文件源代码进行普通压缩,JPG图片可以根据具体质量来压缩为50%到70%,PNG可以使用一些开源压缩软件来压缩,比如24色 ...
- web性能优化——浏览器相关
简介 优化是一个持续的过程.所以尽可能的不要有人为的参与.所以能自动化的或者能从架构.框架级别解决的就最更高级别解决. 这样即能实现面对开发人员是透明的.不响应,又能确保所有资源都是被优化过的. 场景 ...
- Web性能优化系列
web性能优化之重要,这里并不打算赘述.本系列课程将带领大家认识.熟悉.深刻体会并且懂得如果去为不同的站点做性能优化 同时,本系列将还会穿插浏览器兼容性相关问题的解决方案,因为在我看来,兼容性同样属于 ...
- 移动web性能优化笔记
移动web性能优化 最近看了一些文章,对移动web性能优化方法,做一个简单笔记 笔记内容主要出自 移动H5前端性能优化指南和移动前端系列——移动页面性能优化
- web性能优化 来自《web全栈工程师的自我修养》
最近在看<web全栈工程师的自我修养>一书,作者是来自腾讯的前端工程师.作者在做招聘前端的时候问应聘者web新能优化有什么了解和经验,应聘者思索后回答“在发布项目之前压缩css和 Java ...
- web性能优化之--合理使用http缓存和localStorage做资源缓存
一.前言 开始先扯点别的: 估计很多前端er的同学应该遇到过:在旧项目中添加新的功能模块.或者修改一些静态文件时候,当代码部署到线上之后,需求方验收OK,此时你送了一口气,当你准备开始得意于自己的ma ...
- Web 性能优化:Preload与Prefetch的使用及在 Chrome 中的优先级
摘要: 理解Preload与Prefetch. 原文:Web 性能优化:Preload,Prefetch的使用及在 Chrome 中的优先级 作者:前端小智 Fundebug经授权转载,版权归原作者所 ...
随机推荐
- Navicat for Mysql远程连接数据时报(1045错误)Access denied for user 'root'@'localhost' (using password yes);
原因:用户访问被拒绝,更改用户赋予密码即可 mysql命令行执行语句如下 //使用mysql,读取表信息 //更改用户赋予登录密码 //更新权限 注意点:使用flush privileges是为了刷新 ...
- Jena学习笔记(2)——利用数据库保存本体
注明:本文档是使用Jena2.6.4,数据库为MySQL,数据库驱动版本为mysql-connector-java-5.1.13-bin.jar. 1 Jena的数据库接口 Jena提供了将RDF数据 ...
- libstdc++
一.简介 二.安装 http://www.linuxfromscratch.org/lfs/view/development/chapter05/gcc-libstdc++.html 三.其他 1)l ...
- Mac/Linux 定时运行命令行
想要开机运行的话可以通过 mac 自带的 Automator 将要运行的命令打包成一个app,用后在用户组的“登录时启动”列表里加上那个app. 但是想要定时运行就不能这么做了,要用上一个叫cront ...
- Hibernate占位符?和:及JPA
小结一下hibernate占位符. 1.最常见的?占位符. String hql = "select a from Apple a where a.color=? a.weight>? ...
- CSS 属性 :before && :after的用法,伪类和伪元素的区别
一::before && :after的用法 :before 如同对伪元素的名称一样,:before 是用来给指定的元素的内容前面插入新的内容.举例说明: .before:before ...
- 2014 Super Training #8 B Consecutive Blocks --排序+贪心
当时不知道怎么下手,后来一看原来就是排个序然后乱搞就行了. 解法不想写了,可见:http://blog.csdn.net/u013368721/article/details/28071241 其实就 ...
- Android Studio运行程序出现Session ‘app’: Error Launching activity 解决办法
session "app":error launching activity 一下两种方法,可以轻松解决: 1. 2.把复选框去除:
- Android代码优化----PullToRefresh+universal-image-loader实现从网络获取数据并刷新
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- Android数据存储(一)----SharedPreferences详解
一.Android数据的存储方式: Android系统一共提供了四种数据存储方式.分别是:SharePreference.SQLite.Content Provider和File:此外还有一种网络存储 ...