赞
踩
http缓存指的是: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。
常见的http缓存只能缓存get请求响应的资源,对于其他类型的响应则无能为力,所以后续说的请求缓存都是指GET请求
假设我们请求一次服务器,请求头大小1kb,响应头大小1kb,请求文件10kb。
1次请求流量:12kb
10次请求流量:120kb
N次请求:12*N…
这只是假想的一次请求,但事实上的请求不仅是请求文件,请求客户端也会更多,那么问题就很明显:
1.客户端每次都要请求服务器,浪费流量(比如手机端?)。
2.服务器每次都得提供查找,下载,请求用户基础如果较大,服务器存在较大压力。
3.客户端每次请求完都要进行页面渲染,用户体验较差。
那么我们是否可以将请求的文件存放起来使用,比如使用http缓存。
1.Cache-Control
请求/响应头,缓存控制字段,可以说是控制http缓存的最高指令,要不要缓存也是它说了算。
它有以下常用值
1.1 no-store:所有内容都不缓存
1.2 no-cache:缓存,但是浏览器使用缓存前,都会请求服务器判断缓存资源是否是最新,它是个比较高贵的存在,因为它只用不过期的缓存。
1.3 max-age=x(单位秒) 请求缓存后的X秒不再发起请求,属于http1.1属性,与下方Expires(http1.0属性)类似,但优先级要比Expires高。
1.4 s-maxage=x(单位秒) 代理服务器请求源站缓存后的X秒不再发起请求,只对CDN缓存有效(这个在后面会细说)
1.5 public 客户端和代理服务器(CDN)都可缓存
1.6 private 只有客户端可以缓存
2.Expires
响应头,代表资源过期时间,由服务器返回提供,GMT格式日期,是http1.0的属性,在与max-age(http1.1)共存的情况下,优先级要低。
3.Last-Modified
响应头,资源最新修改时间,由服务器告诉浏览器。
4.if-Modified-Since
请求头,资源最新修改时间,由浏览器告诉服务器(其实就是上次服务器给的Last-Modified,请求又还给服务器对比),和Last-Modified是一对,它两会进行对比。
5.Etag
响应头,资源标识,由服务器告诉浏览器。
6.if-None-Match
请求头,缓存资源标识,由浏览器告诉服务器(其实就是上次服务器给的Etag),和Etag是一对,它两会进行对比。
1.让服务器与浏览器约定一个文件过期时间——Expires(GMT时间格式)。
日常请求对话
第一次请求的时候:
后续请求的时候:
浏览器会先对比当前时间是否已经大于Expires,也就是判断文件是否超过了约定的过期时间。
时间没过,不发起请求,直接使用本地缓存。
时间过期,发起请求,继续上述的浏览器与服务器的谈话日常。
问题:假设Expires已过期,浏览器再次请求服务器,但f.js相比上次并未做任何改变,那这次请求我们是否通过某种方式加以避免?
比如约定时间是一个月,约定时间到了我还没改。
2.让服务器与浏览器在约定文件过期时间的基础上,再加一个文件最新修改时间的对比——Last-Modified与if-Modified-Since
日常请求对话
第一次请求:
后续请求:
Expires未过期,浏览器机智的使用本地缓存,免得挨打。
Expires过期,服务器带上了文件最新修改时间if-Modified-Since(也就是上次请求服务器返回的Last-Modified),服务器将if-Modified-Since与Last-Modified做了个对比。
if-Modified-Since 与Last-Modified不相等,服务器查找了最新的a.js,同时再次返回Expires与全新的Last-Modified
if-Modified-Since 与Last-Modified相等,服务器返回了状态码304,文件没修改过,你还是用你的本地缓存。
问题:浏览器端可以随意修改Expires,Expires不稳定,Last-Modified只能精确到秒,假设文件是在1s内发生变动,Last-Modified无法感知到变化,这种情况下浏览器永远拿不到最新的文件(假想极端情况)。
如果是在淘宝京东这种,每一毫秒都会发生变化,这种方式就很不可取
3.让服务器与浏览器在过期时间Expires+Last-Modified的基础上,再增加一个文件内容唯一对比标记——Etag与If-None-Match。哦对了,我们说Expires有可能被篡改,这里我们再加入一个max-age来加以代替(cache-control其中一个值)。
日常对话
第一次请求:
后续请求:
60S内,不发起请求,直接使用本地缓存。(max-age=60代表请求成功缓存后的60S内不再发起请求,与Expires相似,同时存在max-age优先级要比Expires高,区别后面具体说)
60S后,浏览器带上了if-Modified-Since 与If-None-Match(上次服务器返回来的Etag)发起请求,服务器对比If-None-Match与Etag(不对比if-Modified-Since与Last-Modified了,Etag优先级比Last-Modified高,毕竟更精准)
If-None-Match与Etag不相等,说明a.js内容被修改过,服务器返回最新a.js与全新的Etag与max-age=60与Last-Modified与Expires
If-None-Match与Etag相等,说明a.js文件内容无任何改变,返回304,告诉浏览器继续使用之前的本地缓存。
如下图,服务器Etag与If-None-Match相同,所以返回了状态码304,由于优先级问题,虽然也有if-Modified-Since与Last-Modified,但这里不会对两者做对比。
问题:我们已经可以精确的对比服务器文件与本地缓存文件差异,但其实上面方案的演变都存在一个较大缺陷, max-age或Expires不过期,浏览器无法主动感知服务器文件变化。
1.md5/hash缓存
通过**不缓存html,**为静态文件添加MD5或者hash标识,解决浏览器无法跳过缓存过期时间主动感知文件变化的问题。
为什么这么做?实现原理是什么?
之前的浏览器小老弟与服务器老大哥之间的缓存都是建立在每次请求的文件都是在相同的目录以及相同的文件名,如果目录或者是文件名发生改变的时候就会重新请求,管那些什么失效时间乱七八糟的花里胡哨的东西,所以这个时候就出现了新的解决办法。 就是通过webpack来解决,每次打包的时候生成新的文件,嘿嘿,简单粗暴的解决了上面说的浏览器小老弟无法主动感知服务器文件发生改变的事情
module/js/a-hash1.js与module/js/a-hash2.js是两个完全不同的文件,假想浏览器第一次加载页面,请求并缓存了module/js/a-hash1.js,第二次加载,文件指向变成了module/js/a-hash2.js,浏览器会直接重新请求a-hash2.js,因为这就是两个完全不同的文件,哪里还有什么http缓存文件对比,所以通过这种做法,我们就可以从根本上解决过期时间没到浏览器无法主动请求服务器的问题。因此我们只需要在项目每次发布迭代将修改过的静态文件添加不同的MD5或hash标识就好啦。
注意,这里不推荐缓存html文件(或许有更好的做法,欢迎留言),这样每次html加载渲染都可以感知文件变化,反正文件没变还是使用本地缓存,文件名都变了说明修改过,重新请求缓存就好了。
怎么改?一个个手动去修改?那不得累死。webpack提供了webpack-md5-hash插件,可以帮助开发者在项目发布时自动修改文件标识。
2.CDN
cdn就是构建在网络之上的内容分发,依靠部署在各地的边缘服务器,通过不知道具体什么原理的中心平台的负载均衡,然后进行内容分发,调度等功能模块,使用户可以在就近的服务器获取图片等静态文件,
之前看到一个不错的例子,这里直接拿过来举例说说CDN。
假设多年前我们所在的城市只有一个火车站,每次春运,整个城市的人都得去这个火车站买票,人流量以及购票的需求可想而知有多大,为了缓解这个问题,城市的不同区,都出现了火车票代售点,这样每个区的人都可以就近买票了,火车站总站的压力就这样大大减轻了。
我们可以把每个区的售票点称之为CDN节点,也就是前面所说的代理服务器。简而言之,我们可以把CDN理解成浏览器与服务器之间的临时站点,它会替服务器处理一部分的浏览器请求,从而整理减轻总服务器的压力。
我们可以把CDN的价值归纳为:
1.CDN通过分流的形式,大大减轻源站的访问压力。
2.就像住的区比较偏远,每次买票要去城市中心,而这个区后来有了分站,火车票就可以就近购买一样。CDN也解决了跨地区访问问题,根本上为访问提供了加速。
2.2 什么是CDN缓存
CDN边缘节点缓存数据,当浏览器请求,CDN将代替源站判断并处理此处请求。
日常请求对话
第一次请求:
后续请求:
情况1:CDN节点自己缓存的文件未过期,于是返回了304给浏览器,打回了这次请求。
情况2:CDN节点发现自己缓存的文件过期了,为了保险起见,自己发起请求给了服务器(源站),成功拿回了最新数据,然后再交给与了浏览器。
其实说到这,CDN缓存的问题也跟前面的http缓存一样,CDN缓存时间不过期,浏览器始终被拦截,无法拿到最新的文件。
但是我们回归http缓存问题本质,缓存本身针对于更新频率不高的静态文件,其次,CDN缓存提供了分流以及访问加速其它优势条件。
CDN类似一个平台,是可以通过登录,手动更新CDN缓存的,变相解决了浏览器缓存无法手动控制的问题。
由于我没有用过cdn,但是大概可以猜到cdn可能会遇到一些坑就是源文件修改之后,cdn上的文件未更新的问题,大致有两种解决办法就是给静态文件增加版本号,或者是使用cdn提供商的强制刷新
那么两种http缓存方案就说到这里了,接下来谈谈http缓存其它的一些问题和概念。
浏览器地址栏回车,新开窗口,F5刷新,CTRL+F5刷新等浏览器操作对HTTP缓存的影响
对浏览器的不同行为对缓存的影响做一个总结。
**1.浏览器地址栏回车,或者点击跳转按钮,前进,后退,新开窗口,**这些行为,会让Expires,max-age生效,也就是说,这几种操作下,浏览器会判断过期时间,再考虑要不要发起请求,当然Last-Modified和Etag也有效。
2.F5刷新浏览器,或者使用浏览器导航栏的刷新按钮,这几种,会忽略掉Expires,max-age的限制,强行发起请求,Last-Modified和Etag在这种情况下也有效。
3.CTRL+F5是强制请求,所有缓存文件都不使用,全部重新请求下载,因此Expires,max-age,Last-Modified和Etag全部失效。
在利用fiddler抓包看不同网站缓存时,发现了一个有趣的问题,大部分网站的cache-control都设置的为no-cache。
我在前面说了,no-cache不是不缓存,要缓存,但是浏览器在协商性缓存情况下,都会无条件像服务器发起请求,判断自己的缓存是不是最新,如果是就接着用,不是就请求最新的文件,缓存起来用,以此循环。
那么有必要设置过期时间Expires与max-age吗?有!
当我们第一次浏览一个页面,关闭后,第二次再打开还是属于新开窗口行为,如果设置了缓存时间,新开窗口会走强缓存,可以避免反复的文件下载,加快页面渲染,提升用户体验性。
(其实一开始我是觉得Expires与max-age是不需要设置的,直到我使用pageSpeed对公司网站做了个性能评分,发现还是推荐使用缓存时间,结合上面的话,其实是有道理的)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。