如何使用不同的策略在 Cloudfront 上缓存 API

c4k*_*c4k 0 rest caching amazon-cloudfront

我有多个端点,一些是相对于给定用户的,而另一些是全局的。

我想缓存所有这些,但我正在为这个策略而苦苦挣扎。

GET /me
Run Code Online (Sandbox Code Playgroud)

应该缓存,但每个用户都不同。因此,我可以使用Authorization标头中提供的 URI + 访问令牌的组合进行缓存。

GET /products/1
Run Code Online (Sandbox Code Playgroud)

应该被缓存并且对每个人都是一样的。但它也需要Authorization访问标头。所以我只能缓存 URI。

如何在独特的 CloudFront 分配上实现此行为?似乎您只能拥有一种缓存组合。

谢谢,

Mic*_*bot 5

CloudFront 可以根据不同的路径模式缓存多个缓存键组合。每个缓存行为都有一个特定的路径模式,它将匹配——比如/products/*——然后有一个默认的缓存行为来*匹配其他任何东西。(这是默认创建的,无法删除)。CloudFront 分配支持多达 25 个独特的路径模式,每个模式。AWS Support 可能会增加该限制,但由于每个路径模式都支持*以及?通配符,并且有一个默认值可以捕获其他所有内容,这可能就足够了。

CloudFront 从基本假设出发——除了有限的例外——转发到源的任何内容都可能导致源改变响应。因此,默认情况下,几乎所有内容都从原始请求中删除。

User-Agent,例如,被设定为Amazon CloudFront之前请求被发送到的原点。为什么?因为如果User-Agent允许通过,源可能会根据对用户代理字符串的分析修改内容,例如识别设备类型(例如台式机、移动设备、平板电脑、智能电视)并做出相应的响应。CloudFront 无法事先知道源服务器将如何处理这些值。但是,如果您需要 CloudFront 假设更改用户代理可能会更改响应,则 CloudFront 还需要为它看到的每个用户代理字符串缓存每个对象的唯一副本,并仅使用这些缓存的副本来匹配另一个相同的请求。您可以将其列入白名单User-Agent用于转发到源的标头,这就是发生的情况:CloudFront 然后将该标头与每个请求一起发送,并添加User-Agent缓存键中——这是事物的集合,始终包括请求路径,并且始终包括列入白名单的标头CloudFront 可用于唯一标识它应该认为真正相同的任何未来请求。¹

Cookie 和查询字符串参数也可能导致源服务器修改其响应,因此默认情况下,这些参数也会从请求中删除。您可以指定哪些 cookie,或所有 cookie,或无(默认)。您可以指定哪些查询字符串参数,或所有查询字符串参数,或无(默认)。您指定的任何内容都会添加到缓存键中,并转发到源,并且 CloudFront 将仅提供与整个缓存键完全匹配的缓存响应。

Authorization头就是一个特别有趣的例子,因为它似乎你已经发现了一个问题,但可能被忽视的另一这是非常重要的。

在这种情况下GET /me-- 每个Authorization提交请求的唯一用户(由 标识)都会得到不同的响应。路径的缓存行为设置/me需要将Authorization标头列入白名单。很容易。

但是呢GET /products/1?这里是龙。您仍然必须将Authorization标头转发到源,否则 CloudFront 实际上并不知道它是否是有效的授权请求。尽管直觉表明可以使用缓存响应,因为每个授权用户都应该收到相同的响应...... CloudFront 不能这样做,因为您需要源来验证是否可以对特定Authorization标头做出有利响应。它必须发送到源,这意味着它必须是缓存键的一部分。每个唯一且有效的 Authorization标头值都会导致 CloudFront 获取并缓存我们希望重用的响应的新副本。如果完全相同的用户再次请求它,它实际上只会被重用,并且具有相同的Authorization 标题。

但是,对于我们需要根据请求的某些属性对请求进行身份验证/授权的情况,CloudFront 有一个潜在的解决方案,但我们不想通过将Authorization标头或 cookie转发到源来稀释缓存命中潜力,从而增加它们到缓存键。

Lambda@Edge 是一项 CloudFront 增强功能,允许您在 CloudFront 信号流中的 4 个战略点拦截、检查和可能修改请求和响应——在请求端,检查缓存之前和之后,以及响应在缓存写入之前和最终响应(无论是命中还是未命中)返回给查看器之前。HTTP 请求和/或响应转换为 JavaScript 数据结构,您的自定义 Node.js“触发器”代码可以修改 CloudFront 的行为。

在您的情况下,Lambda@Edge查看器请求触发器似乎是一种解决方案。

查看器请求触发器可以访问原始请求,包括标头和 cookie 以及 CloudFront 将剥离的查询参数,因为它们不是缓存键的一部分。

就在这里,对于/products/*,您将逻辑嵌入到触发器函数代码中以验证Authorization标头。您将触发器功能分配给/products/*缓存行为。

如果Authorization有效,您让请求通过并将控制权返回给 CloudFront,如果可用,它将从缓存中提供服务,否则从您的源服务器请求 - 但不存在Authorization标头,因为对于这些路径,您不会转发它,所以它不在缓存键中。³ 您的响应现在可以缓存和重用。

如果Authorization标头无效,您将直接在触发器代码中生成拒绝响应,CloudFront 将您的响应返回给未经授权的请求者,不对对象进行缓存检查。

但是如何Authorization从触发器函数中验证标头?这取决于您的平台如何运作。如果是JWT,可以直接在函数代码中验证。但是 Lambda@Edge 环境可以访问 Internet,与 CloudFront 目前正在处理的请求分开,因此一种选择可能是直接向您的服务器发送 HTTP 请求。另一个可能涉及您发送到 DynamoDB 之类的服务的查找。这是高度特定于实现的。

Lambda@Edge 函数在可重复使用的容器中运行。虽然不能保证重用,但观察表明会发生大量重用,因此Authorization在全局对象的内存中缓存头查找的结果可以预期具有很高的命中率。

当然,权衡是此解决方案的成本、复杂性和增加的延迟是否超过了降低资源使用和减少延迟(可能由潜在的高缓存命中率导致)的好处。


¹ 有很多可能的用户代理值,这User-Agent对于转发到源(因此包括在缓存键中)通常是一个非常糟糕的选择,因此 CloudFront 团队为这种特定情况提出了解决方案。CloudFront 可以为您分析用户代理,并将浏览器分类为桌面、移动非平板电脑、移动平板电脑或智能电视User-Agent,您可以将一个或多个CloudFront-Is-*-Viewer标头列入白名单,而不是将其列入白名单......从而获得能力根据正在使用的浏览器的一般类别提供响应,并显着提高缓存命中率,如果User-Agent被转发。这些浏览器分类标题不是用户代理字符串成为缓存键的一部分,而是成为缓存键的一部分,因此只有 4 个唯一组合。(理论上,由于每个值都是布尔值,假设全假是不可能的,有 15 种可能的组合,但我从未遇到过超过 4 种。)

² 查询字符串参数还有一个选项,即“全部转发,基于白名单缓存”。这是一般规则的一个例外,即发送到源的所有内容都是缓存键的一部分。类似的选项不适用于请求标头或 cookie。

³ 那么你怎么知道请求是你的源应该处理的东西,因为它没有Authorization标题?您在 CloudFront 中注入一个自定义源头,其中包含一个秘密的静态密钥,只有您的源和 CloudFront 知道,从而建立信任。