谈谈http缓存

708 阅读5分钟

缓存存在意义

当前服务器没有配置缓存,刷新页面就会重新请求资源,如果设置了缓存就能从内存或者磁盘中获取资源从而达到页面加载优化。

强缓存

本质上是通过请求响应头来实现的。

pragma

progma 是 HTTP1.0 时期的产物,和后面要说的 cache-control 作用差不多,它的值只能设置为 no-cache。与 Cache-Control: no-cache 效果一致,即禁用强缓存,只能使用协商缓存。

expires

如果在Cache-Control响应头设置了 "max-age" 或者 "s-max-age" 指令,那么 Expires 头会被忽略,也就是说优先级 cacahe-control 大于 expires。

因为 expires 是一个时间值,如果服务器和客户端是系统时间差较大,就会引起缓存混乱。

cache-control

cache-control 这个头部字段既可以用在请求头也可以用在响应头中。

cache-control 作为响应头字段其实是对 expires 做了改进,cache-control 其中的一种值形式为 cache-control: max-age=seconds,例如:cache-control: max-age=315360000。seconds 是一个时间差而不是固定的时间,因为是时间差, 所以不存在上面提到的 expires 的客户端和服务器端时间不同步导致缓存混乱的问题。

强缓存优先级

pragma > cache-control > expires。

具体流程

首先,当浏览器发现了内存或者磁盘有你请求的资源缓存时,此时浏览器还会检查该资源上一次请求时的有没有返回上面叙述的和强缓存相关的响应头。

根据上面说的和强缓存相关的首部字段的优先级,一步一步判断。可能有些字段服务器不会返回,比如 pragma,那就直接往后判断。

具体就是:如果 pragma: no-cache,那强缓存直接就判断失败了,只能走协商缓存。

如果没有 pragma,但是有 cache-control: no-cache,这就和 pragma: no-cache 一样,强缓存判断失败。

如果 cache-control: max-age=seconds,那么此时就根据浏览器上次请求该资源的时间和 seconds 算出过期时间,如果早于过期时间也就是未过期,那么命中强缓存,如果过期了强缓存判定失败。

前面说过了如果 control-control 值为 max-age 或者 s-max-age 那么 expires 直接就无效了。当 cache-control 值不是那两个或则没有时,还要根据 expires 值也就是过期时间来判定有没有过期,没有过期就命中强缓存,否则,缓存失效,向服务器请求最新资源。

使用 expires 配置强缓存

服务器代码大致如下,其实就是在响应头中加上了 expires 字段,过期时间为 1 分钟后。

长按刷新选择第三个清缓存首次加载

刷新再次加载

使用 cache-control 配置强缓存

设置 cache-control: max-age=60,理论上效果应该是缓存1分钟后失效,事实证明确实如此。

首次加载

刷新再次加载

协商缓存

协商缓存由于需要向服务器发送一次请求,所以相比于强缓存来收收益更低,缓存资源体积越大,收益越高。

协商缓存中那几个首部字段是配对使用的即:

请求头 if-modified-since 和响应头 last-modified 请求头 if-none-match 和响应头 etag

if-modified-since 和 last-modified

含义:自从某某时间有没有修改过?,最后一次修改时间为某某时间。 本次请求头 if-modified-since的值应该为上一次请求该资源的响应头中 last-modified 的值。

当浏览器发起资源请求并携带 if-modified-since 字段,服务器会将请求头中的 if-modified-since 值和请求资源的 最后修改时间进行比较,如果资源最后修改时间比 if-modified-since 时间晚,那么资源过期,状态码为 200,响应体为请求资源,响应头中加入最新的 last-modified 的值。没过期就返回状态码 304,命中协商缓存,响应体为空,响应头不需要 last-modified 值。

if-none-match 和响应头 etag

etag 表示的是所请求资源的唯一标识符,简单来实现就是给请求的资源通过某种 hash 算法取个摘要字符串然后用双引号包起来就好了。

为什么有了 last-modified 还需要 etag

资源在 1 秒内更新,并且在该一秒内访问,使用 last-modified 处理协商缓存无法获取最新资源。本质上的原因还是因为 last-modified 是精确到秒的,无法反映在 1 秒内的变化。

当资源多次被修改后内容不变,使用 last-modified 来处理有点浪费。多次修改资源,其 last-modified 值肯定是会变的,但是如果内容不变我们其实不需要服务器返回最新资源,直接使用本地缓存。使用 etag 就没这个问题,因为同一个资源多次修改,内容一样,hash 值也一样。

使用 etag 更加灵活,因为 etag 并不一定是我说的就用 hash 值,etag 采用的是弱比较算法,即两个文件除了每个比特都相同外,内容一致也可以认为是相同的。例如,如果两个页面仅仅在页脚的生成时间有所不同,就可以认为二者是相同的。

协商缓存首部字段优先级

if-none-match > if-modified-since。

last-modified 配置协商缓存

1.长按刷新选择第三个清缓存首次加载

2.刷新再次加载

etag 配置协商缓存

这里就随便设置返回的etag测试

1.长按刷新选择第三个清缓存首次加载

2.刷新再次加载

总结