缓存问题: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将最新的副本提供给前端
  • 编辑器然后对帖子进行更改并通过CCD_ 6将其提交给后端
  • 编辑器然后使用Command-R(或CTRL-R)刷新浏览器选项卡
  • 因此,前端随后再次请求GET /posts/5,但在更改之前从获取过期副本,因为距离上次GETPATCH之后发布的GET还没有过去180秒

我想要的体验是:

  • 编辑器加载帖子页面,然后调用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的任何后续调用都将从那里得到服务,直到可以从后端获得更新的版本。

我的问题是:

  • 可以用";突变";通过GET /posts/5透明地为变异对象服务
  • 这个突变能在浏览器标签页刷新后存活下来吗?或者浏览器关闭、重新打开和随后的/GET posts/5
  • 有其他模式/最佳实践可以解决这个问题吗

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


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

这种技术被称为缓存破坏。我不确定这是否是最好的做法,但我很确定它已经被广泛应用,因为它很容易理解。

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

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

  1. 通常情况下,您只需要为所有用户请求GET /posts/<id>
  2. 当用户登录时,会根据任何算法生成哈希密钥。为了简单起见,我们只使用递增整数,并将其称为version。您将此version存储在localStorage中,这样它就可以在页面刷新后继续存在
  3. 现在,当用户查看自己的帖子或其他人的帖子时,您需要区分场景。当一个人在看自己的时,你总是用GET /posts/<id>?version=n
  4. 每当用户编辑他的帖子并点击保存按钮时,您就会将versionn更改为n+1
  5. 下次他转到post-view页面时,应用程序会请求未缓存的GET /posts/<id>?version=n+1,并检索最新内容
  6. 最后一件事是,确保您的服务器安全地忽略?version=n查询字符串

我相信这个问题还有其他解决方案。我不是服务器配置和HTTP头的专家,所以我不想讨论这个话题,但一定有什么需要寻找的。

作为纯粹的前端解决方案,有Serivece Worker API供您考虑。这个API的要点是使开发人员能够通过编程控制缓存策略。

有了这个API,你可以将当前的应用程序代码保留为as-is,只需安装一个服务工作者,然后你可以在后台使用相同的缓存破坏技术来获取新内容,或者在用户编辑时删除缓存(使用cache API),甚至从用户刚刚发送的PATCH /posts/<id>中伪造GET /posts/<id>的响应。

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

对于流量很大但更新很少的网站来说,这非常有效,而且实现起来非常简单。对于有很多作者和频繁更改内容的网站,你需要更有创意。

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

最新更新