这正是数据验证令牌(ETag)想要解决的问题,服务端生成并返回的这个数据指纹令牌,通常就是返回数据的 Hash 值或者其他数据指纹,客户端无需关心它的生成规则,只需要知道它是当前数据的一个唯一标识。
客户端需要在下次请求时将其通过 If-None-Match 这个请求报文头,将此验证令牌发送至服务端,如果数据令牌指纹和服务端当前的数据一致,则标识资源未发生新的变化。就会返回一个 304 的状态码,表示可以继续使用客户端本地缓存的数据,并刷新超时时间。注意当响应码为 304 的时候,它是不包含数据内容的。
通常此缓存操作对我们都是透明的,它是浏览器和开源网络库的基本实现,我们无需自己去判断 max-age 和 ETag 的值,这一步我们只需要确定服务端对此有支持即可。
这里只是提到了 If-None-Match,它标识比较 ETag 是否不一致,除此之外,还有一些其他的相关报文头,例如 If-Match,有兴趣可以查阅相关资料。
2.5 Cache-Control前面举的例子中,我们只为 Cache-Control 设定了一个 max-age,但是其实还有一些更丰富的配置。
从缓存性能最优化的角度来看,最佳的缓存是无需与服务端通信的缓存,可以通过缓存来消灭网络延迟以及数据请求,从而来提高用户的体验。
Cache-Control 是在 HTTP/1.1 中被定义的,它可以用于取代之前的缓存策略,现在所有的浏览器都支持 Cache-Control ,它已经成为一种通用的标准。
Cache-Control 还有一些更灵活的配置,用来对缓存做一些更细致的操作。
1. “no-cache” 和 “no-store”
这两个参数都表示每一次请求,都需要真实的发送一个网络请求。
它们之间的区别在于,“no-cache”并不是真的不缓存数据,它只是要求每次都确认资源是否过期,也就是它会利用数据令牌 ETag 来一定程度的减小传输的流量。
而 “no-store” 完全是要求客户端,每次都重新请求数据并下载最新的数据,不做任何缓存处理。这种不缓存的策略,也包括中间连接的代理、网关 等中间传输的通道,也一并不对数据进行缓存,每次都从源服务器上获取数据。
2. “public” 和 “private”
“public” 是一种默认的策略,表示当前缓存是开放的,任何请求响应的中间环节,都可以对其进行缓存,如果我们不显式指定,则当前为 “public” 缓存。
与之相对的 “private”,则表示当前响应是针对单个用户的,并非通用数据,因此不建议任何中间缓存对其进行缓存。例如:浏览器就是一个比较私人的缓存源,它会缓存 “private” 的缓存,而 CDN 则不会。
三、最佳的缓存策略树前面提到,缓存的核心目的就是为了快,能让下次使用的时候快速复用。所以在理想情况下,我们应该将响应数据尽可能多的缓存,尽可能的缓存足够长的时间,并且为每个资源提供单独的数据验证令牌,以便在时间过期之后快速校验。
但是任何事情都是要取其平衡点的,不存在什么最佳缓存策略,并非所有响应资源都需要加缓存,这就需要根据业务场景来设定。
这里给出一个增加 HTTP 缓存的通用策略树,你在对响应增加缓存的时候,可以参考它来执行。
正常情况下,我们针对不同的响应属性,会对它设置不同的缓存策略,下面根据场景,举几个例子。
3.1 用户相关的数据和单个用户紧密相关的数据,通常我们是不建议使用缓存的,但是依然存在几个等级。
1. 严格不使用缓存
Cache-Control:no-store2. 允许客户终端缓存,但是每次使用都需要确认
Cache-Control:no-cache ETag:"cxmyDev1234"3. 允许客户终端短时间缓存
Cache-Control:private max-age=600 ETag:"cxmyDev1234" 3.2 通用数据一些通用响应资源,更新的频率非常的低,我们可以根据需要调整 max-age 的大小即可。
Cache-Control:max-age=86400 ETag:"cxmyDev1234" 四、废弃和更新缓存的响应