关于缓存策略
自动化的缓存机制(基于服务端和客户端协商的)
分级缓存策略(3层):
(1层)200状态 : 当浏览器本地没有缓存或者下一层失效时,或者用户点击了ctl+f5时,浏览器直接去服务器下载最新数据
(2层)304状态 : 这一层由last-modified/etag控制。当下一层失效时或用户点击refresh,F5时,浏览器就会发送请求给服务器,如果服务端没有变化,则返回304给浏览器
(3层) 这一层由expires/cache-control控制(1)expires(http1.0版本有效)是绝对时间(2)cache-control(http1.1版本有效),相对时间,两者都存在时cache-control覆盖expires只要没有失效,浏览器只访问自己的缓存
缓存策略
浏览器的缓存一般存在内存或者磁盘中(为了方便理解,我们暂时称他为浏览器缓存库)
- 内存缓存(memory cache)
一般将脚本,图片,字体等等和页面产生交互的部分存放在内存中
原因:利于性能提升
- 磁盘内存(disk cache)
一般将css等这些不经常变动的数据存放在磁盘中进行缓存
而缓存规则一般区分为是否需要重新向服务器发起请求来分类,一般分为2类
以下是关于2种缓存下的顺序图
1. 强缓存
强缓存分为两类
from disk cache 缓存在硬盘,浏览器关闭,缓存还在
from memory cache 缓存在内存,浏览器关闭,缓存清除
存在缓存数据,并且命中缓存
缓存未命中
2. 协商缓存
存在缓存数据,协商缓存后命中
存在缓存数据,协商缓存未命中
从上面可以看出一旦强缓存生效则不会再向服务器请求。而协商缓存则是不管是否生效都需要和服务端发送交互
当两类缓存同时存在的时候,强制缓存优先级大于协商缓存,也就是说一旦强缓存生效,则不会再执行协商缓存
对于强缓存来说,响应头header中会有两个字段(Expires/Cache-control)
在谷歌浏览器中可以看到如下
Expires
Expires是HTTP1.0的产物。Expires的值为服务端返回的到期时间,下一次请求的时候如果请求时间小于服务端返回的时间则直接使用缓存。
这里存在的缺点是到期时间是根据服务端生成的,但是传到客户端的时间可能和服务端想要传的时间存在偏差,这就会导致缓存命中出现误差。
故而现在HTTP1.1的版本基本使用Cache-control代替
Cache-Control
Cache-Control是最重要的规则,常见的值有以下(默认private):
值 | 作用 |
---|---|
private | 表示响应可以被客户端缓存 |
public | 表示响应可以被客户端和代理服务器缓存 |
no-store | 所有内容都不会缓存(协商缓存,强缓存都不执行) |
no-cache | 会使用协商缓存来验证缓存数据 |
max-age | 指定缓存的有效时间(在有效的时间内不会再发起请求) |
s-maxage | 覆盖 max-age,作用一样,只在代理服务器中生效 |
max-stale=30 | 30s内,即使缓存过期,也使用该缓存 |
max-fresh=30 | 希望在30s内获取最新的响应 |
协商缓存 (实现前提是使用Cache-Control:no-cache)
顾名思义,协商缓存就是需要通过和服务端进行判断,之后再决定是否使用的缓存。当浏览器没有命中强缓存,就会向服务器发送请求,看是否命中协商缓存,如果命中,则服务器不会返回文件,而是返回httpcode304,浏览器从本地缓存加载文件。
具体的流程是:
- 浏览器第一次请求数据的时候,服务器会将标识符和数据一起返回给客户端,客户端将二者备份缓存至
浏览器缓存库
中 -
当再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断后 成功则返回304,通知浏览器可以使用缓存,否则返回200直接请求数据
对于协商缓存,需要着重理解的是缓存标识。它在请求header和响应header之间进行传递。以下就是关于这两种方式的解释:
1. last-Modified/If-Modified-Since(http1.0响应头和请求头)
Last-Modified:
服务器在响应请求时,告诉浏览器资源的最后修改的时间戳(Last-Modified
会含在Response Header中。例如:
Last-Modified:Web ,25 Oct 2019 16:48:00 GMT
If-Modified-Since:
再次请求服务器的时候,通过此字段携带上次请求服务器返回的Last-Modified
的时间戳,以last-Modified-Since为key存储时间戳,请求服务器
服务器收到请求后,发现有If-Modified-Since,则与被请求资源的最后修改时间作对比
如果最后修改的时间大于If-Modified-Since,说明资源有被改动,则会更新该部分的资源,然后返回200
否则,说明资源没有更新,则返回304,告知浏览器继续使用保存的缓存
值得注意的是,如果我们并没有配置缓存策略,浏览器会将响应头的Date减去Last-Modified再乘0.1,作为我们的资源缓存时间
缺点:
由于Last-Modified If-Modified-Since 组合是http1.0的规则(比较老旧了)所以也有十分明显的缺点:
因为侦测改变的时间最小单位为1s,意味着如果在1s的时间内请求资源发生了改变也会正常触发缓存,导致客户端无法获取到最新资源
2. Etag/If-None-Match (优先级高于Last-Modified/If-Modified-Since)
就因为Last-Modified/If-Modified-Since 存在的缺点。故而当前这个方式则能解决前者的缺点。
Etag
Etag是服务端根据请求资源的内容所生成的一种类似于Token的标识(hash值码),
服务器响应请求时,会在Response Header
中种下一个Etag的标签,值为服务端为资源生成的唯一标识
而下次发起对该资源的请求时,请求头会携带If-None-Match
字段,该字段携带着上次服务端返回的Etag
的值
服务端接收到If-None-Match之后,会和该资源的标识进行对比,
相同则认为资源未发生改变响应304,客户端自动使用资源缓存
反之,则会响应200,且发送最新资源
优点:
缓存精度高,不会出现上面提到过的缺点。
缺点:
由于Etag需要
服务端使用特定算法判断资源变化情况
所以占用的资源比较多,性能上不如Last-Modified/If-Modified-Since 方式快
但是还是推荐使用Etag,因为如今服务器性能大大提升
而且大多数服务器都采用了负载均衡
不同虚拟机返回的Last-Modified时间戳不一致,导致对比失败
总结
- 强制缓存:服务器通知浏览器一个缓存时间,在缓存时间内下次请求直接使用缓存,不在时间内,执行
协商缓存
策略 - 协商缓存:将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,分别返回304 或者 200 再执行相应操作