【问题标题】:Cache issues: React + REST server behind CDN缓存问题:CDN 后面的 React + REST 服务器
【发布时间】:2021-03-24 13:31:39
【问题描述】:

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

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

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

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

这是我现在的一个场景:

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

我想要的体验是:

  • 编辑器加载帖子页面,然后调用GET /posts/5
  • CDN 将最新副本提供给前端。
  • 然后编辑对帖子进行更改并通过PATCH /posts/5 将其提交到后端。
  • 在 Command-R 浏览器选项卡刷新后,GET /posts/5 会立即返回带有编辑器对 PATCH 所做更改的数据副本,无论 ttl 需要 180 秒才能生成新副本获得。
  • 对于其他用户,当GET /posts/5

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

我的问题是:

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

【问题讨论】:

  • @hackape 提出的解决方案是一种潜在的前进方式。但是,我们也可以在写入操作时突发 CloudFront 缓存。您可以尝试类似的方法-stackoverflow.com/questions/22021651/…
  • @MukulBansal 好信息!但我认为这只会破坏 CloudFront CDN 节点上的缓存,而不是浏览器中的缓存。
  • @MukulBansal 是正确的.. 如果浏览器从 CDN 获取一个 ttl 为 5 分钟的对象,它甚至不会在接下来的 5 分钟内与 CDN 对话。它只会为内存/磁盘中的对象提供服务——并且使 CDN 上的对象无效不会改变这一点。
  • @JasonGenX 好的。所以这里有 3 件事 - 1. 爆破 CDN 缓存 - 这可以按照上面讨论的方式完成。 2. 为帖子所有者爆裂浏览器缓存——这在你手中。一旦编辑修补了帖子,您可以在服务器成功响应后清除他的浏览器缓存。 3. 为非帖子所有者爆裂浏览器缓存 - 根据您的场景,如果不爆裂,您就可以了。不过,如果你想爆破它,你可以通过套接字。维护所有活跃用户的列表是一件繁重的事情。 Facebook 或 Bitbucket 就是这样做的。

标签: reactjs swr


【解决方案1】:

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,然后您可以在后台使用相同的缓存破坏技术来获取新内容,或者只是删除缓存(使用 @987654323 @) 当用户编辑时,甚至从用户刚刚发送的PATCH /posts/<id> 中伪造对GET /posts/<id> 的响应。

【讨论】:

    【解决方案2】:

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

    对于流量很大但更新很少的网站,这很有效,而且实施起来也很简单。但是,对于拥有大量作者且内容经常更改的网站,您需要获得更多创意。

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

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 2011-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-27
      • 2010-12-24
      相关资源
      最近更新 更多