【问题标题】:CDN support/configuration for serving "stale" content, refreshing in background提供“陈旧”内容的 CDN 支持/配置,在后台刷新
【发布时间】:2020-05-05 20:51:11
【问题描述】:

目标

始终提供来自 CDN EDGE 缓存的内容,无论内容多么陈旧。尽可能在后台刷新它。

问题

我有一个NextJS 应用程序,它在服务器端呈现一些 React 组件并将它们交付给客户端。对于这个讨论,让我们只考虑我的主页,它是未经身份验证的,对每个人来说都是一样的。

我希望将服务器呈现的主页缓存在 CDN 的 EDGE 节点上,并尽可能频繁地或总是从该缓存中向终端客户端提供服务。

根据我的阅读,正确支持缓存相关标头设置(如 Surrogate-ControlCache-Control: stale-while-revalidate)的 CDN(如 Fastly)应该能够做到这一点,但实际上,我并没有看到它像我一样工作会期待的。我看到了:

  • 请求会错过缓存并在先前的请求应该对其进行预热时返回到源
  • 请求从缓存中提供,但在源发布新内容时永远不会更新

示例

考虑以下时间表:


[T0] - 访客 1 请求 www.mysite.com - CDN 缓存完全冷,因此请求必须返回我的源 (AWS Lambda) 并重新计算主页。返回带有标题Surrogate-Control: max-age=100Cache-Control: public, no-store, must-revalidate 的响应。 然后访问者 1 被提供主页,但他们不得不等待高达 5 秒! YUCK!愿没有其他访客会遭受同样的命运。

[T50] - 访问者 2 请求 www.mysite.com - CDN 缓存包含我的文档并立即将其返回给访问者。他们只需要等待 40 毫秒!惊人的。在后台,CDN 从我的源站重新获取最新版本的主页。原来它没有改变。

[T80] - www.mysite.com 将新内容发布到主页,使任何缓存的内容真正过时。该网站的 V2 现已上线!

[T110] - 访客 1 返回到www.mysite.com - 从 CDN 的角度来看,距离访客 2 的请求仅 60 秒,这意味着访客 2 发起的后台刷新应该导致

[T160] - 访客 3 访问 www.mysite.com - 尽管是新访客,但 CDN 缓存现在从访客 1 最近触发的后台刷新中恢复。为 Visitor3 提供一个缓存的 V2 主页。

...

只要每 100 秒至少有 1 位访问者访问我的网站(因为 max-age=100),没有访问者会遭受到我的原点的完整往返等待时间。


问题

1. 这是对现代 CDN 的合理要求吗?我无法想象这比总是返回原点(没有 CDN 缓存)更费力,但我一直在努力从任何 CDN 提供商那里找到有关正确方法的文档。我现在正在使用 Fastly,但也愿意尝试其他任何方法(我先尝试了 Cloudflare,但读到他们不支持 stale-while-revalidate

2. 什么是正确的标题? (假设 CDN 提供商支持它们) 我在 Fastly 和 Cloudflare 中使用了 Surrogate-Control: maxage=<X>Cache-Control: public, s-maxage=<X>, stale-while-revalidate,但似乎没有一个能正确地做到这一点(在 maxage 时间范围内的请求不会在原点上发生变化,直到缓存未命中)。

3. 如果不支持,是否有 API 调用可以让我将内容更新推送到我的 CDN 的缓存层,有效地说“嘿,我刚刚发布了此缓存键的新内容. 在这里!”

我可以使用 Cloudflare worker 来使用他们的 KV 存储自己实现这种缓存,但我想在为似乎很常见的问题实现代码解决方案之前,我需要做更多的研究。

提前致谢!

【问题讨论】:

  • @jamisOn 你最终找到了一个好的解决方案吗?

标签: cdn next.js cloudflare cache-control fastly


【解决方案1】:

我最近一直在部署一个类似的应用程序。我最终在 Next.js 服务器前运行了一个自定义的 nginx 实例。

  • 忽略来自上游服务器的缓存头。
    • 我想缓存标记和 JSON,但我不想将 Cache-Control 标头发送到客户端。您可以调整此配置以使用来自 Next.js 的 Cache-Control 中的值,然后如果 MIME 类型为 text/htmlapplication/json,则在响应客户端之前删除该标头。
  • 考虑所有有效期为 10 分钟的回复。
  • 30 天后删除缓存的响应。
  • 缓存最多使用 800 MB。
  • 提供过时的响应后,尝试从上游服务器获取新的响应。

这并不完美,但它可以处理重要的 stale-while-revalidate 行为。如果您想要全局传播的好处,您也可以在此之上运行 CDN。

警告:这还没有经过广泛的测试。我不确定围绕错误页面和响应代码的所有行为是否正确。

# Available in NGINX Plus
# map $request_method $request_method_is_purge {
#   PURGE   1;
#   default 0;
# }

proxy_cache_path
  /nginx/cache
  inactive=30d
  max_size=800m
  keys_zone=cache_zone:10m;

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  # Basic
  root /nginx;
  index index.html;
  try_files $uri $uri/ =404;

  access_log off;
  log_not_found off;

  # Redirect server error pages to the static page /error.html
  error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 500 501 502 503 504 505 /error.html;

  # Catch error page route to prevent it being proxied.
  location /error.html {}

  location / {
    # Let the backend server know the frontend hostname, client IP, and
    # client–edge protocol.
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    # This header is a standardised replacement for the above two. This line
    # naively ignores any `Forwarded` header passed from the client (which could
    # be another proxy), and instead creates a new value equivalent to the two
    # above.
    proxy_set_header Forwarded "for=$remote_addr;proto=$scheme";

    # Use HTTP 1.1, as 1.0 is default
    proxy_http_version 1.1;

    # Available in NGINX Plus
    # proxy_cache_purge $request_method_is_purge;

    # Enable stale-while-revalidate and stale-if-error caching
    proxy_cache_background_update on;
    proxy_cache cache_zone;
    proxy_cache_lock on;
    proxy_cache_lock_age 30s;
    proxy_cache_lock_timeout 30s;

    proxy_cache_use_stale
      error
      timeout
      invalid_header
      updating
      http_500
      http_502
      http_503
      http_504;

    proxy_ignore_headers X-Accel-Expires Expires Cache-Control Vary;
    proxy_cache_valid 10m;

    # Prevent 502 error
    proxy_buffers 8 32k;
    proxy_buffer_size 64k;
    proxy_read_timeout 3600;

    proxy_pass "https://example.com";
  }
}

【讨论】:

    【解决方案2】:

    关于问题 3。
    您可以为进行更改的每个文件使用新名称 以防止不需要的缓存或使用 API cloudFlare 像您建议的那样释放缓存。
    有可能,您可以在这里找到更多信息
    https://api.cloudflare.com/#zone-purge-files-by-cache-tags-or-host

    【讨论】:

    • 3,仅适用于 PUSH CDN。 Cloudflare 不是一个,它是 PULL。 Stale-while-revalidate 应该与 Fastly 一起使用,也许可以联系他们的支持,他们非常棒。 Cloudflare 也不以有用的方式支持 Stale while revalidate,这也是我们使用 Fastly 进行测试的原因之一。
    猜你喜欢
    • 2021-07-23
    • 2017-10-10
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 2019-07-10
    • 2016-10-11
    • 1970-01-01
    • 2015-08-31
    相关资源
    最近更新 更多