缓存标头和利用边缘网络加速访问

2024 年 3 月 11 日 星期一(已编辑)
/ ,
348
3

本文以 Cloudflare + Vercel 为例。

了解缓存标头

在生产中,我们通常会利用 Redis 去缓存接口数据。对于前端应用来说,我们一般会利用内容分发网络(CDN)去缓存页面和其他静态资源。

我们知道 HTTP 标头 cache-control 可以用来强缓存资源。

例如,cache-control: max-age=60 表示资源在 60 秒内不会过期,浏览器如果不使用强制刷新的话(例如请求标头含有cache-control: max-age=0)可以直接使用本地缓存。

在边缘网络或是内容分发网络中,一般都能相关的标头进行缓存。

比如:s-maxage 的值。

Note

The s-maxage response directive indicates how long the response remains fresh in a shared cache. The s-maxage directive is ignored by private caches, and overrides the value specified by the max-age directive or the Expires header for shared caches, if they are present.

Cache-Control: s-maxage=604800

参考:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#s-maxage

而这个值,一般都被 CDN 或者边缘网络用来控制缓存时间。

在 Vercel 中。Cache 章节中,告诉我们可以使用这个值去控制一个 API 响应的缓存时间。另外也提供了 Vercel 私有的 HTTP 标头,例如 Vercel-CDN-Cache-Control

在 Cloudflare 中,When to use CDN-Cache-Control 中的例子可以更好的帮助理解资源在各端的缓存控制。

Headers:

Cache-Control: max-age=14400, s-maxage=84000
Cloudflare-CDN-Cache-Control: max-age=24400
CDN-Cache-Control: max-age=18000

Cache behavior:

CachesCache TTL (seconds)
Origin Server Cache14400
Network Shared Cache84000
Cloudflare Edge24400
Other CDNs18000
Browser Cache14400

上表中,Cloudflare 可以消费响应头中的 cache-control: s-maxageCloudflare-CDN-Cache-Control 控制资源在 Cloudflare 分发网和边缘网络的缓存。

通过缓存标头,可以更快的让用户访问到资源,减少服务器的负载。

以 Vercel 部署的 Next.js 为例,下面我们来做一些优化,让短期并发的负载下减少服务器的真实请求。

Next.js API Route 的响应缓存

这是一个简单的 Next.js API Route。

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export const GET = async (req: NextRequest) => {
  return NextResponse.json({})
}

现在我们可以增加缓存标头。

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export const GET = async (req: NextRequest) => {
  return NextResponse.json(
    {},
    {
      headers: {
        'Cache-Control': 'max-age=60, s-maxage=86400',
        'CDN-Cache-Control': 'max-age=86400',
      },
    },
  )
}

现在把 API 使用 Cloudflare 代理。

在第一次请求之后,下一次请求中,我们能看到 Cloudflare 命中了缓存。

Cf-Cache-Status: HIT

使用 vercel.json 全局配置标头

在单个 API 中,每每的添加缓存标头非常的麻烦并且我们也无法控制除了 API Route 之外的其他路由。我们可以使用 vercel.json 全局配置标头。

在项目根目录下新建 vercel.json,内容如下:

下面路由参考项目 Shiro,可按需配置路由。

{
  "headers": [
    {
      "source": "/",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "s-maxage=300, stale-while-revalidate=59"
        },
        {
          "key": "CDN-Cache-Control",
          "value": "max-age=300"
        },
        {
          "key": "Vercel-CDN-Cache-Control",
          "value": "max-age=300"
        }
      ]
    },
    {
      "source": "/og",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "s-maxage=3600, stale-while-revalidate=30"
        },
        {
          "key": "CDN-Cache-Control",
          "value": "max-age=3600"
        },
        {
          "key": "Vercel-CDN-Cache-Control",
          "value": "max-age=3600"
        }
      ]
    },
    {
      "source": "/api/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "s-maxage=60, stale-while-revalidate=30"
        },
        {
          "key": "CDN-Cache-Control",
          "value": "max-age=60"
        },
        {
          "key": "Vercel-CDN-Cache-Control",
          "value": "max-age=60"
        }
      ]
    },
    {
      "source": "/feed",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "s-maxage=86400, stale-while-revalidate=3600"
        },
        {
          "key": "CDN-Cache-Control",
          "value": "max-age=86400"
        },
        {
          "key": "Vercel-CDN-Cache-Control",
          "value": "max-age=86400"
        }
      ]
    }
  ]
}

使用 Vercel 提供的配置,可以对每个路由进行缓存标头的配置。配置过后,Vercel 会在响应这些请求时,自动添加缓存标头,然后经过 Cloudflare 读取缓存标头,然后在 Cloudflare 网络进行缓存,在下次请求到达时,直接从 Cloudflare 缓存中读取。

stale-while-revalidate= 的值控制了一个资源在过期后,多久内仍然可以使用过期资源,然后在后台异步更新资源,那么在即便过期的场景下,用户也能快速的获取到资源,加快页面加载。

Important

路由的匹配是从下到上的,那么也就是说,第一项可以定义为全局 Fallback 也就是路由匹配 /(.*)

参考:https://vercel.com/docs/projects/project-configuration#headers

Vercel 边缘网缓存

你可能也注意到 Vercel-CDN-Cache-Control 这个标头。这个标头是 Vercel 私有的标头,用来控制资源在 Vercel 边缘网络的缓存。既然 Vercel 也提供了缓存,为什么还要使用 Cloudflare 呢?

我想大概有下面的原因:

  1. Cloudflare 一般作为 DNS 提供商,他会有着更加快速的响应时间。
  2. Vercel 的缓存命中很奇怪,有时候会命中,有时候不会命中,让我更费解;而 Cloudflare 的缓存命中符合文档,在意料之中。
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...