缓存问题:CDN 后面的 React + REST 服务器

问题描述

我正在寻找一种模式,可以让我为我的用户改善用户体验。我有一个在 CloudFront 后面运行的 REST 服务器,它被前端的普通 React 应用程序使用。

我将简化我的例子来说明我的问题。

我有一个名为 GET /posts/<id> 的端点。当浏览器请求它时,它带有一个 max=age=180,这意味着它将被存储在浏览器的缓存中,并且在这 180 秒的持续时间内对 GET /posts/<id> 的任何后续调用都将从浏览器的缓存中提供,之后它将再次访问 CDN 以尝试获取新副本。

这对大多数用户来说没问题。我不介意任何帖子的更新在传播给所有用户之前最多延迟 3 分钟。但是有一位用户是这篇文章的作者。该用户可以使用 PATCH /posts/<id> 更改此帖子。我们称该用户编辑者

这是我现在的场景:

  • 编辑器加载帖子页面,然后调用 GET /posts/5
  • CDN 将最新副本提供给前端。
  • 然后编辑器对帖子进行更改并通过 PATCH /posts/5 将其提交到后端。
  • 然后编辑器使用 Command-R(或 CTRL-R)刷新浏览器选项卡。
  • 因此,前端再次请求 GET /posts/5 -- 但从 before 更改获取过时的副本,因为自上次 {{1 }} 和 GETGET
  • 之后发出

我想要的体验是:

  • 编辑器加载帖子页面,然后调用 PATCH
  • CDN 将最新副本提供给前端。
  • 然后编辑器对帖子进行更改并通过 GET /posts/5 将其提交到后端。
  • 在 Command-R 浏览器选项卡刷新后,PATCH /posts/5 会立即带回包含编辑器对 GET /posts/5 所做更改的数据副本,无论 PATCH 的 180 秒如何在获得新副本之前。
  • 至于其他用户,他们完全可以等待 180 秒,然后当 ttl

我正在使用 Axios,但我不认为 SWR 和 React-Query 支持突变。据我了解,这将允许编辑器为他在服务器上 GET /posts/5对象声明一个突变,以便他对 PATCH'ed 进行的任何后续调用都将从那里提供服务,直到更新的版本可以从后端获取

我的问题是:

  • 带有“mutations”的 SWR 能否通过 GET /posts/5 透明地为变异对象提供服务?
  • 突变能否在浏览器标签页硬刷新后幸存下来?还是浏览器关闭、重新打开和随后的 GET /posts/5
  • 是否有其他模式/最佳实践可以解决这个问题?

解决方法

TL;DR:只需在请求的末尾附加一个无害的、乱码的查询字符串 GET /posts/<id>?version=whatever


好问题。我必须承认我不知道这个问题的完整答案,但我想在前端开发人员中分享一项众所周知的技术。

该技术称为cache busting。我不确定这是否是最佳实践,但我很确定它被广泛实践,因为它非常容易理解。

想法很简单。当您在末尾添加更改后的查询字符串时,您有效地更改了 URL,因此不会命中缓存,从而避免了整个缓存问题。

因此,针对您的特定用例的解决方案的详细步骤如下:

  1. 通常,您只需为所有用户请求 GET /posts/<id>
  2. 当用户登录时,无论什么算法都会生成一个哈希键。为简单起见,我们只使用递增的整数并将其称为 version。您将此 version 存储在 localStorage 中,以便它可以在页面刷新后继续存在。
  3. 现在您需要区分用户查看自己的帖子或其他人的帖子时的场景。当男人在看他自己的时候,你总是用 GET /posts/<id>?version=n
  4. 每当用户编辑他的帖子并点击保存按钮时,您就会将 versionn 撞到 n+1
  5. 下次他去发布查看页面时,应用请求未缓存的 GET /posts/<id>?version=n+1,并会检索最新内容。
  6. 最后一件事,请确保您的服务器安全地忽略该 ?version=n 查询字符串。

我确信还有其他解决方案可以解决这个问题。我不是服务器配置和 HTTP 标头方面的专家,所以我不会进入该主题,但必须要寻找一些东西。

作为纯前端解决方案,有 Serivce Worker API 供您考虑。此 API 的主要目的是使开发人员能够以编程方式控制缓存策略。

使用此 API,您可以保持当前的应用程序代码不变,只需安装一个 Service Worker,然后您就可以在后台使用相同的缓存破坏技术来获取新内容,或者只是删除缓存(使用 {{ 3}}) 当用户编辑,甚至伪造来自用户刚刚发送的 GET /posts/<id>PATCH /posts/<id> 的响应。

,

根据您使用的 CDN,您可以在发布帖子更新时手动使缓存失效。例如,cloudfront 可让您指定要在下一个请求中重新获取的路径。

对于流量大但更新很少的网站,这非常有效,而且实施起来非常简单。不过,对于拥有大量作者且内容经常变化的网站,您需要更具创意。

我过去使用的一种策略是使用一种称为对象版本控制的技术,在该技术中,您只需发布带有时间戳的对象版本,而不是使对象的缓存失效。这也意味着您需要在前端加载时发布清单文件。清单包含页面需要加载的所有内容的最新时间戳,并且其 TTL 比其余内容短得多。当您发布帖子的新版本时,您将更新清单中的时间戳,前端会在下次加载页面时提取最新版本。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...