缓存问题:CDN 背后的 React + REST 服务器
Cache issues: React + REST server behind CDN
我正在寻找一种可以让我为用户改善用户体验的模式。我在 CloudFront 后面有一个 REST 服务器 运行 被前端的普通 React 应用程序使用。
我将简化示例以说明我的问题。
我有一个名为 GET /posts/<id>
的端点。当浏览器请求它时,它带有一个 max=age=180
,这意味着它将存储在浏览器的缓存中,并且在这 180 秒的持续时间内,任何对 GET /posts/<id>
的后续调用都将从浏览器的缓存中提供, 之后它将再次访问 CDN 以尝试获取新副本。
大多数用户都可以。我不介意任何 post 的更新在传播给所有用户之前最多延迟 3 分钟。但是有一位用户是此 post 的作者。该用户可以使用 PATCH /posts/<id>
对此 post 进行更改。我们称该用户为编辑.
这是我现在的场景:
- 编辑器加载 post 页面,然后调用
GET /posts/5
- CDN 将最新副本提供给前端。
- 然后编辑器对 post 进行了更改,并通过
PATCH /posts/5
将其提交到后端。
- 然后编辑器使用 Command-R(或 CTRL-R)刷新浏览器选项卡。
- 因此,前端随后再次请求
GET /posts/5
-- 但在 更改之前从 获取陈旧的副本,因为从那以后还没有过去 180 秒在 PATCH
之后发布的最后一个 GET
和 GET
我想要的体验是:
- 编辑器加载 post 页面,然后调用
GET /posts/5
- CDN 将最新副本提供给前端。
- 编辑然后对 post 进行更改并通过
PATCH /posts/5
将其提交到后端。
- 在 Command-R 浏览器选项卡刷新后,
GET /posts/5
立即带回数据副本以及编辑器使用 PATCH
所做的更改,而不管 [=24] 的 180 秒=] 在获得新副本之前。
- 至于其他用户,当
GET /posts/5
时,他们可以等待 180 秒,然后 post 中的更改传播给他们
我正在使用 Axios,但我不认为 SWR 和 React-Query 支持突变。据我了解,这将允许编辑器在服务器上为他刚刚 PATCH'ed
的对象声明一个突变,这样他对 GET /posts/5
所做的任何后续调用都将从那里提供服务,直到更新的版本可以从后台获取。
我的问题是:
- 具有“突变”的 SWR 能否通过
GET /posts/5
透明地为突变对象提供服务?
- 突变会在硬浏览器选项卡刷新后继续存在吗?或浏览器关闭、重新打开和随后的
/GET posts/5
?
- 是否有另一种 pattern/best 做法可以解决这个问题?
TL;DR:只需在请求末尾附加一个无害的、乱码的查询字符串 GET /posts/<id>?version=whatever
好问题。我必须承认我不知道这个问题的完整答案,但我想与前端开发人员分享一项 well-known 技术。
该技术称为 cache busting。我不确定这是否是最佳实践,但我很确定它已被广泛实践,因为它 straight-forward 太难理解了。
想法很简单。当您将更改的查询字符串添加到末尾时,您实际上更改了 URL,因此没有命中缓存,您避免了整个缓存问题。
因此针对您的特定用例的解决方案的详细步骤如下:
- 通常您只需为所有用户请求
GET /posts/<id>
- 当用户登录时,会通过任何算法生成哈希键。为简单起见,我们只使用递增整数并将其称为
version
。您将此 version
存储在 localStorage 中,以便它可以通过页面刷新继续存在。
- 现在您需要区分用户正在查看自己的 post 或其他人的 post 的情况。男生看自己的时候,你总是用
GET /posts/<id>?version=n
- 每当用户编辑他的 post 并点击保存按钮时,您会将
version
从 n
提升到 n+1
- 下次他转到 post 查看页面时,应用程序请求
GET /posts/<id>?version=n+1
未缓存,并将检索 up-to-date 内容。
- 最后一件事,确保您的服务器安全地忽略
?version=n
查询字符串。
我相信这个问题还有其他解决方案。我不是服务器配置和 HTTP 方面的专家 headers,所以我不会进入那个主题,但一定有一些东西需要寻找。
对于纯前端解决方案,有 Serivce Worker API 供您考虑。 API 的要点是使开发人员能够以编程方式控制缓存策略。
有了这个 API,您可以保留当前的应用程序代码 as-is,只需安装一个 Service Worker,然后您就可以在后台使用相同的缓存清除技术来获取新内容,或者只需在用户编辑时删除缓存(使用 Cache API),甚至可以从用户刚刚发送的 PATCH /posts/<id>
伪造对 GET /posts/<id>
的响应。
根据您使用的 CDN,您可以在向 post 发布更新时手动使缓存失效。例如 cloudfront 允许您指定要在下一个请求中获取新鲜的路径。
对于流量大但更新很少的站点,此方法效果很好,而且实施起来非常简单。对于有很多作者和经常更改内容的网站,您需要更有创意。
我过去使用的一种策略是使用一种称为对象版本控制的技术,在该技术中,您不会使对象的缓存失效,而只需发布带有时间戳的对象版本。这也意味着您需要在前端加载时发布清单文件。清单包含页面需要加载的所有内容的最新时间戳,并且 TTL 比其余内容短得多。当您发布 post 的新版本时,您将更新清单中的时间戳,并且前端会在下次加载页面时提取最新版本。
我正在寻找一种可以让我为用户改善用户体验的模式。我在 CloudFront 后面有一个 REST 服务器 运行 被前端的普通 React 应用程序使用。
我将简化示例以说明我的问题。
我有一个名为 GET /posts/<id>
的端点。当浏览器请求它时,它带有一个 max=age=180
,这意味着它将存储在浏览器的缓存中,并且在这 180 秒的持续时间内,任何对 GET /posts/<id>
的后续调用都将从浏览器的缓存中提供, 之后它将再次访问 CDN 以尝试获取新副本。
大多数用户都可以。我不介意任何 post 的更新在传播给所有用户之前最多延迟 3 分钟。但是有一位用户是此 post 的作者。该用户可以使用 PATCH /posts/<id>
对此 post 进行更改。我们称该用户为编辑.
这是我现在的场景:
- 编辑器加载 post 页面,然后调用
GET /posts/5
- CDN 将最新副本提供给前端。
- 然后编辑器对 post 进行了更改,并通过
PATCH /posts/5
将其提交到后端。 - 然后编辑器使用 Command-R(或 CTRL-R)刷新浏览器选项卡。
- 因此,前端随后再次请求
GET /posts/5
-- 但在 更改之前从 获取陈旧的副本,因为从那以后还没有过去 180 秒在PATCH
之后发布的最后一个
GET
和 GET
我想要的体验是:
- 编辑器加载 post 页面,然后调用
GET /posts/5
- CDN 将最新副本提供给前端。
- 编辑然后对 post 进行更改并通过
PATCH /posts/5
将其提交到后端。 - 在 Command-R 浏览器选项卡刷新后,
GET /posts/5
立即带回数据副本以及编辑器使用PATCH
所做的更改,而不管 [=24] 的 180 秒=] 在获得新副本之前。 - 至于其他用户,当
GET /posts/5
时,他们可以等待 180 秒,然后 post 中的更改传播给他们
我正在使用 Axios,但我不认为 SWR 和 React-Query 支持突变。据我了解,这将允许编辑器在服务器上为他刚刚 PATCH'ed
的对象声明一个突变,这样他对 GET /posts/5
所做的任何后续调用都将从那里提供服务,直到更新的版本可以从后台获取。
我的问题是:
- 具有“突变”的 SWR 能否通过
GET /posts/5
透明地为突变对象提供服务? - 突变会在硬浏览器选项卡刷新后继续存在吗?或浏览器关闭、重新打开和随后的
/GET posts/5
? - 是否有另一种 pattern/best 做法可以解决这个问题?
TL;DR:只需在请求末尾附加一个无害的、乱码的查询字符串 GET /posts/<id>?version=whatever
好问题。我必须承认我不知道这个问题的完整答案,但我想与前端开发人员分享一项 well-known 技术。
该技术称为 cache busting。我不确定这是否是最佳实践,但我很确定它已被广泛实践,因为它 straight-forward 太难理解了。
想法很简单。当您将更改的查询字符串添加到末尾时,您实际上更改了 URL,因此没有命中缓存,您避免了整个缓存问题。
因此针对您的特定用例的解决方案的详细步骤如下:
- 通常您只需为所有用户请求
GET /posts/<id>
- 当用户登录时,会通过任何算法生成哈希键。为简单起见,我们只使用递增整数并将其称为
version
。您将此version
存储在 localStorage 中,以便它可以通过页面刷新继续存在。 - 现在您需要区分用户正在查看自己的 post 或其他人的 post 的情况。男生看自己的时候,你总是用
GET /posts/<id>?version=n
- 每当用户编辑他的 post 并点击保存按钮时,您会将
version
从n
提升到n+1
- 下次他转到 post 查看页面时,应用程序请求
GET /posts/<id>?version=n+1
未缓存,并将检索 up-to-date 内容。 - 最后一件事,确保您的服务器安全地忽略
?version=n
查询字符串。
我相信这个问题还有其他解决方案。我不是服务器配置和 HTTP 方面的专家 headers,所以我不会进入那个主题,但一定有一些东西需要寻找。
对于纯前端解决方案,有 Serivce Worker API 供您考虑。 API 的要点是使开发人员能够以编程方式控制缓存策略。
有了这个 API,您可以保留当前的应用程序代码 as-is,只需安装一个 Service Worker,然后您就可以在后台使用相同的缓存清除技术来获取新内容,或者只需在用户编辑时删除缓存(使用 Cache API),甚至可以从用户刚刚发送的 PATCH /posts/<id>
伪造对 GET /posts/<id>
的响应。
根据您使用的 CDN,您可以在向 post 发布更新时手动使缓存失效。例如 cloudfront 允许您指定要在下一个请求中获取新鲜的路径。
对于流量大但更新很少的站点,此方法效果很好,而且实施起来非常简单。对于有很多作者和经常更改内容的网站,您需要更有创意。
我过去使用的一种策略是使用一种称为对象版本控制的技术,在该技术中,您不会使对象的缓存失效,而只需发布带有时间戳的对象版本。这也意味着您需要在前端加载时发布清单文件。清单包含页面需要加载的所有内容的最新时间戳,并且 TTL 比其余内容短得多。当您发布 post 的新版本时,您将更新清单中的时间戳,并且前端会在下次加载页面时提取最新版本。