利用“在重新验证时过时”功能保持内容的新鲜度

这是一个额外的工具,可帮助您在分发 Web 应用时平衡即时性和新鲜度。

stale-while-revalidate 可帮助开发者在即时性(立即加载缓存的内容)和新鲜度(确保将来使用缓存内容的更新)之间取得平衡。如果您维护的第三方 Web 服务或库会定期更新,或者您的第一方素材资源通常具有较短的生命周期,那么 stale-while-revalidate 可能是现有缓存政策的理想补充。

Chrome 75Firefox 68 支持在 Cache-Control 响应标头中同时设置 stale-while-revalidatemax-age

不支持 stale-while-revalidate 的浏览器将静默忽略该配置值,并使用 max-age,我稍后很快会加以说明...

我们将 stale-while-revalidate 分解为两部分:缓存的响应可能已过时这一概念,以及重新验证的过程。

首先,浏览器如何知道缓存的响应是否“过时”?包含 stale-while-revalidateCache-Control 响应标头也应包含 max-age,并且通过 max-age 指定的秒数决定了过时性。任何高于 max-age 的缓存响应都会被视为全新响应,较早的缓存响应会过时。

如果本地缓存的响应仍然新鲜,则可以直接使用该响应来满足浏览器的请求。从 stale-while-revalidate 的角度来看,在这种情况下无需执行任何操作。

但是,如果缓存的响应已过时,系统会执行另一项基于时间的检查:缓存响应的时长是否在 stale-while-revalidate 设置提供的额外时间范围内?

如果过时响应的年龄段在该时间范围内,则系统会使用该响应来满足浏览器的请求。同时,对网络发出的“重新验证”请求不会延迟缓存响应的使用。返回的响应可能包含与之前缓存的响应相同的信息,也可能不同。无论是哪种方式,网络响应都会存储在本地,取代之前缓存的所有内容,并重置日后进行的任何 max-age 比较期间使用的“新鲜度”计时器。

不过,如果缓存的过时响应已过时,超出了 stale-while-revalidate 时间范围,则无法满足浏览器的请求。浏览器将改为从网络检索响应,并将其用于执行初始请求,同时使用新响应填充本地缓存。

实例

以下是一个简单的 HTTP API 示例,用于返回当前时间,更具体地说,是当前小时过后的分钟数。

在这种情况下,Web 服务器会在其 HTTP 响应中使用此 Cache-Control 标头:

Cache-Control: max-age=1, stale-while-revalidate=59

此设置表示,如果在接下来的 1 秒内重复请求时间,之前缓存的值仍会保持新鲜状态,并按原样使用,而无需任何重新验证。

如果在 1 到 60 秒后重复发出请求,则缓存的值将过时,但仍会用于执行 API 请求。与此同时,“在后台”,系统会发出重新验证请求,以便使用新值填充缓存以供日后使用。

如果在 60 秒后重复请求,则系统不会使用过时响应,并且满足浏览器请求和缓存重新验证都将取决于从网络获取响应。

下面详细介绍了这三种不同的状态,以及在我们的示例中每种状态适用的时间范围:

展示上一部分中信息的示意图。

常见用例有哪些?

虽然上面的“小时过后几分钟”API 服务示例是人为构建的,但它说明了预期的用例:提供需要刷新的信息,但可以接受一定程度的过时信息的服务。

不那么牵强附会的示例可能是当前天气状况的 API,或过去一小时内撰写的头条新闻。

通常,任何以已知间隔更新、可能会被多次请求且在该间隔内保持静态的响应都非常适合通过 max-age 进行短期缓存。除了 max-age 之外,使用 stale-while-revalidate 有助于提高从缓存中使用更新内容来满足未来请求的可能性,而不会阻塞网络响应。

它如何与 Service Worker 交互?

如果您听说过 stale-while-revalidate,很可能是在 服务工件中使用的食谱的上下文中。

通过 Cache-Control 标头使用“在重新验证期间使用过时数据”与在服务工件中使用该功能有许多相似之处,并且关于新鲜度权衡和最大生命周期的许多注意事项也适用。不过,在决定是实现基于服务工件的方案,还是仅依赖 Cache-Control 标头配置时,您需要考虑以下几点。

在以下情况下,请使用服务工件方法:

  • 您已在 Web 应用中使用 Service Worker。
  • 您需要对缓存内容进行精细控制,并希望实现类似于最近最少使用过期政策的功能。Workbox 的缓存过期时间模块可以帮助您解决此问题。
  • 您希望在重新验证步骤中,当过时响应在后台发生更改时收到通知。Workbox 的广播缓存更新模块可以帮助实现这一点。
  • 您需要在所有现代浏览器中实现此 stale-while-revalidate 行为。

在以下情况下,请使用 Cache-Control 方法:

  • 您不想处理为 Web 应用部署和维护 Service Worker 的开销。
  • 您可以让浏览器的自动缓存管理功能防止本地缓存过大。
  • 您可以接受目前并非所有现代浏览器都支持的方法(截至 2019 年 7 月;未来可能会增加支持的方法)。

如果您使用的是 Service Worker,并且还通过 Cache-Control 标头为某些响应启用了 stale-while-revalidate,那么通常情况下,Service Worker 将有机会先响应请求。如果服务工件决定不响应,或者在生成响应过程中使用 fetch() 发出网络请求,则最终会采用通过 Cache-Control 标头配置的行为。

了解详情

主打图片由 Samuel Zeller 提供。