聊一聊HTTP缓存
HTTP 缓存作为 Web 重要的一环,理解并合理使用它能够提高 Web 性能。
1 前言
什么是 HTTP 缓存
为什么需要 HTTP 缓存,因为使用 HTTP 缓存可以减少请求响应时间和避免网络时延带来的等待时间
如何合理使用缓存,设置不同文件的缓存时间
2 缓存的种类
缓存分为两种,Private cache和Shared cache
Private cache是针对单个用户而言,一个例子就是自己的浏览器缓存,使用这些缓存实现了浏览器的前进/后退,而无需再次请求资源
Shared Cache针对多个用户,比如代理服务器,一个 ISP (Internet Service Provider)或者公司都有代理服务器,用来缓存一些资源,供所有员工访问,而不必再向互联网发出请求,避免网络延迟
3 缓存控制
要清楚缓存控制,这里要先分清两个概念,一个叫做缓存存储(Cache Storage),一个叫做缓存(Caching)
其实在中文里面,可能直接都说成缓存,但是这样不助于理解下面缓存控制的前两种情况。其实缓存存储是一个实实在在的东西,而缓存只是它的一个动作
后来我又发现国人有强缓存和协商缓存的说法,强缓存就是指Cache Storage会直接返回 Cache 的内容,协商缓存是指Cache Storage向原始服务器进行验证,如果原始服务器返回304 Not Modified,Cache Storage才返回 Cache 的内容
上面一幅图中,304 Not Modified表示使用了协商缓存,而from memory cache和from disk cache则直接使用强缓存
Chrome 在某个版本之后将以前的from cache变成了from disk cache和from memory cache,就跟名字一样,缓存在磁盘中的内容即使在关闭了当前 tab 之后还能使用,但是缓存到内存的内容,当关闭 tab 或者浏览器,或者浏览器崩溃后,就消失了。但是memory cache加载速度更快,从上图也能看出
关于强缓存和协商缓存,我没发现这两个词的英文出处,但是这两个词理解起来更形象,所以下面的缓存控制情况我也会用到这两个词
3.1 1. 不使用缓存存储
可以理解为每次的请求都不经过 Cache Storage,直接发给服务器,返回新的对象
|
|
3.2 2. 不直接使用缓存内容
Cache Storage 会缓存内容,但是每次需要向服务器验证资源是否过期,没过期才能使用缓存,这就是304 Not Modified的情况,也就是我们所说的协商缓存
|
|
3.3 3. 公共缓存和私有缓存
例如告诉资源只能被Private Browser Cache缓存起来还是同时也能被Public Proxy Server缓存起来
|
|
3.4 4. 强缓存过期控制
Cache-Control
优先级高于Expired
,前者是相对时间,后者是绝对时间;前者为General Header的字段,后者为Response Header的字段
|
|
3.5 5. 资源验证
|
|
4 缓存验证
缓存验证发生在用户刷新,或者响应头中包含Cache-Control: must-revalidate
的情况
缓存验证有两种形式,都可以用来验证文档是否过期
- 文档的最后修改日期,响应头Last-Modified字段,请求头If-Modified-Since字段请求验证
- 对文档当前版本的唯一标识,叫做entity tag,响应头ETag字段,通常使用 MD5 Hash 实现,请求头If-None-Match字段请求验证
同时,缓存验证有两种验证类型
- 强验证(strong validator),文档对比一个字节一个字节地进行,严格对比
- 弱验证(weak validator),文档只有语义不同才认为是改变,例如一个 HTML 文档也许只是里面的广告发生改变,或者是页脚的日期发生改变,弱验证会认为它们还是相同的
HTTP 默认使用的是强验证
需要注意的是,一般情况下,Last-Modified字段只能作为弱验证,因为它的最小单位是秒,一秒内文档可能改变 2 次,无法做到标识每一次改变,如果使用它做强验证,则需要明确知道不会发生这种情况。当然,还有一些其他的限制,具体的限制可以看我下面的第 4 条 RFC 文档参考,这里就不细说了
而ETag默认作为强验证,如果需要用它实现弱验证比较困难,因为需要判断文档中不同元素的语义,确保语义变化时ETag的值才发生变化,而不是每次检测到字节变化时就生成一个新的值
缓存验证这部分建议有能力的同学直接查看第 4 条 RFC 参考
5 条件缓存(响应头增加Vary字段)
该字段指出了筛选条件,如果下一次到达Cache Storage的请求中的该条件与缓存时的该条件的值不一样,则重新向真实 server 发请求,否则使用缓存,看下面这幅图
Vary字段通常会对User-Agent进行验证,避免将移动端缓存的文档直接发给桌面端请求的用户,反之亦然
|
|