HTTP 缓存策略

2019-09-25 · 1,348 chars · 7 min read

本文是一篇总结,梳理 HTTP 的缓存策略。

背景是期望直接使用该策略,优化客户端 Native 的网络访问,所以总结了相关内容分享给服务端和客户端开发。

1. 缓存判断基本流程#

1.1. 第一次请求#

第一次请求,没有缓存,单纯的客户端请求,服务端响应,如图:

Note

上图虽然写着浏览器,但都是 Http Client,客户端同理

1.2. 第二次请求#

第二次请求,此时客户端已有缓存:

  1. 固定缓存判断,是否过期。未过期直接响应,不需要发请求。
  2. 已过期,进入协商阶段,是否存在 ETag,有则执行 ETag 协商,304 响应。
  3. 已过期且没有 ETag,是否存在 Last-Modified,有则执行 Last-Modified 协商,304 响应。
  4. 固定缓存过期,也没有字段协商(或者协商后发现缓存过期),直接响应新资源,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. 客户端缓存建议#

  1. 对于时间不敏感的数据,可以使用固定缓存+协商缓存
Cache-Control: max-age=300, must-revalidate, private
ETag: "8fd46984b642637f5c39a09c1dc28f9f"
  1. 不太频繁更新的数据,可以使用协商缓存
Cache-Control: no-cache, must-revalidate, private
ETag: "8fd46984b642637f5c39a09c1dc28f9f"
  1. 频繁更新的数据,不建议缓存(除非是离线环境使用)
  2. CDN 不仅可以加速静态资源,也可以用在接口上,直接使用 CDN 的缓存策略配置接口

5. 参考#

赞赏

微信