HTTP 缓存策略
2019-09-25 · 1,348 chars · 7 min read
本文是一篇总结,梳理 HTTP 的缓存策略。
背景是期望直接使用该策略,优化客户端 Native 的网络访问,所以总结了相关内容分享给服务端和客户端开发。
1. 缓存判断基本流程#
1.1. 第一次请求#
第一次请求,没有缓存,单纯的客户端请求,服务端响应,如图:
Note
上图虽然写着浏览器,但都是 Http Client,客户端同理
1.2. 第二次请求#
第二次请求,此时客户端已有缓存:
- 固定缓存判断,是否过期。未过期直接响应,不需要发请求。
- 已过期,进入协商阶段,是否存在 ETag, 有则执行 ETag 协商,304 响应。
- 已过期且没有 ETag,是否存在 Last-Modified,有则执行 Last-Modified 协商,304 响应。
- 固定缓存过期,也没有字段协商(或者协商后发现缓存过期),直接响应新资源,200 响应。
2. 缓存控制#
上面是比较完整的缓存判断流程,具体流程该怎么执行,是由 Http 头信息中的某些字段控制的:
2.1. Cache-Control#
http/1.1 定义 Cache-Control 头,主要用来区分对缓存机制的支持情况。
Cache-Control 取代了之前用来定义响应缓存策略的标头(例如 Expires)。 所有现代浏览器都支持 Cache-Control,所以就不需要关心旧的标头了。
这里只列一些比较常用的
可缓存性相关:
value | 描述 |
---|---|
public | 表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容(例如,该响应没有max-age指令或Expires消息头) |
private | 表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容 |
no-cache | 在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证 |
no-store | 缓存不应存储有关客户端请求或服务器响应的任何内容 |
到期相关:
value | 描述 |
---|---|
max-age=<seconds> | 设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与Expires相反,时间是相对于请求的时间 |
s-maxage=<seconds> | 覆盖max-age或者Expires头,但是仅适用于共享缓存(比如各个代理),私有缓存会忽略它 |
max-stale[=<seconds>] | 表明客户端愿意接收一个已经过期的资源。可以设置一个可选的秒数,表示响应不能已经过时超过该给定的时间 |
重新验证和重新加载相关:
value | 描述 |
---|---|
must-revalidate | 一旦资源过期(比如已经超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求 |
proxy-revalidate | 与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略 |
2.2. ETag & If-None-Match#
涉及缓存协商(缓存验证)的表头主要是 ETag & If-None-Match 和 Last-Modified & If-Modified-Since。后两个基本不用了,现在主要使用 ETag。
ETag 可以理解为资源(文件)指纹,只有资源真正改变的时候,ETag 才会改变。而 Last-Modified 则是基于修改时间做校验的,对于静态资源,如果是全量发布,资源即使没变,也会被覆盖一次,导致最后修改时间改变,缓存失效。
2.3. 例子#
2.3.1. 例1:固定缓存#
用 octolinker 的接口举例:
这个接口直接使用固定缓存,耗时200ms,缓存 5min,第二次请求时:
不发请求,直接从缓存响应,耗时 1ms。
2.3.2. 例2:固定缓存+协商缓存#
大厂的 web、h5 接口,没找到使用缓存的,这里看 v2ex 的例子:
这个接口结合了固定缓存+协商缓存,首先客户端缓存 60min:
第二次请求的时候,直接使用本地缓存,注意 etag 值是没有变的。
等本地缓存过期后,再次请求,此时使用 ETag 进行协商:
经过服务端校验,缓存依然有效,响应 304
3. 最佳缓存策略#
(图片来自 https://web.dev/http-cache/ )
上图是一个比较完整的决策树,可根据具体的业务需求,制定最佳的缓存策略。
例如对于静态资源(image,js,css):
- 固定缓存 cache-control: max-age,命中缓存直接使用,节省一次请求
- 固定缓存失效后,使用 ETag 协商。资源未改变,响应一个较小的 304,否则完整响应 200
4. 客户端缓存建议#
- 对于时间不敏感的数 据,可以使用固定缓存+协商缓存
Cache-Control: max-age=300, must-revalidate, private ETag: "8fd46984b642637f5c39a09c1dc28f9f"
- 不太频繁更新的数据,可以使用协商缓存
Cache-Control: no-cache, must-revalidate, private ETag: "8fd46984b642637f5c39a09c1dc28f9f"
- 频繁更新的数据,不建议缓存(除非是离线环境使用)
- CDN 不仅可以加速静态资源,也可以用在接口上,直接使用 CDN 的缓存策略配置接口