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

时间:2019-02-07 13:23:53

标签: rest caching amazon-cloudfront

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

我想缓存所有这些,但是我在策略上苦苦挣扎。

GET /me

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

GET /products/1

应该被缓存,并且每个人都相同。但是它也需要Authorization标头才能被访问。所以我只能缓存URI。

如何在唯一的CloudFront发行版上实现此行为?看来只能有一个缓存组合。

谢谢

1 个答案:

答案 0 :(得分:2)

CloudFront可以基于不同的路径模式对多个缓存键组合进行缓存。每个缓存行为都有一个特定的路径模式可以匹配-例如/products/*-然后*有一个默认的缓存行为匹配其他任何内容。 (此默认情况下已创建,无法删除)。 CloudFront发行版最多支持25个唯一的路径模式。可能有可能使AWS Support增加该限制,但是由于每个路径模式都支持*?通配符,并且有一个默认值可以捕获其他所有内容,因此就足够了。

CloudFront是基于基本假设(有限的例外情况)工作的,即转发到原点的任何内容都可能导致原点改变响应。因此,默认情况下,几乎所有内容都从原始请求中删除。

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

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

Authorization标头是一个非常有趣的示例,因为您似乎已经发现了一个问题,但是却忽略了另一个非常重要的问题。

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

但是GET /products/1呢?这是龙。您还必须将Authorization标头转发到源,因为CloudFront否则实际上并不知道这是否是有效的授权请求。即使直觉表明可以使用缓存的响应,但由于每个授权用户都应该收到相同的响应... CloudFront无法做到这一点,因为您需要来源来验证是否可以对特定的{{1} }标头。必须将其发送到源,这意味着它必须是缓存键的一部分。每个唯一的和有效 Authorization头值都会导致CloudFront提取并缓存我们希望重用的响应的新副本。实际上,只有在完全相同的用户使用相同的Authorization标头再次请求时,它才会被重用。

但是,对于需要基于请求的某些属性对请求进行身份验证/授权的情况,CloudFront有一个潜在的解决方案,但我们不想通过转发Authorization标头来减少缓存命中的可能性或cookie到原始位置,从而将它们添加到缓存键中。

Lambda @ Edge是一种CloudFront增强功能,可让您在检查缓存之前和之后,在CloudFront信号流中的4个战略要点截取,检查和潜在地修改请求和响应-在请求侧在响应方面,在将缓存写入之前和最终响应(命中或未命中)返回给查看器之前。 HTTP请求和/或响应被转换为JavaScript数据结构,并且您自定义的Node.js“触发”代码可以修改CloudFront的行为。

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

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

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

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

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

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

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

当然,要权衡的是此解决方案的成本,复杂性和增加的延迟是否超过了潜在的高缓存命中率所带来的资源使用减少和延迟减少的好处。


¹那里的用户代理值很多,以至于Authorization对于转发到源(因此包括在缓存密钥中)通常是一个非常糟糕的选择,因此CloudFront团队想出了一个此特定情况的解决方案。 CloudFront可以为您分析用户代理,并将浏览器分类为台式机,移动非平板电脑,移动平板电脑或智能电视,您可以将一个或多个{{1 }}标头...使得能够基于所使用的浏览器的一般类别来传递响应,并显着提高了缓存命中率,如果转发User-Agent,则命中率会非常低。这些浏览器分类标头不是高速缓存密钥的一部分,而是用户代理字符串,而是高速缓存密钥的一部分,仅产生4个唯一的组合。 (从理论上讲,由于每个值都是布尔值,因此有15种可能的组合假设不可能为全false,但我从未遇到超过4个。)

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

³那么您如何知道请求是您的原始请求处理的内容,因为它没有User-Agent标头?您将一个自定义来源标头注入CloudFront内,该标头包含一个秘密的静态密钥,只有您的来源和CloudFront才知道,以建立信任。