CloudFront / S3 ETag:CloudFront 是不是可以在 CF TTL 过期之前发送更新的 S3 对象?

Posted

技术标签:

【中文标题】CloudFront / S3 ETag:CloudFront 是不是可以在 CF TTL 过期之前发送更新的 S3 对象?【英文标题】:CloudFront / S3 ETag: Possible for CloudFront to send updated S3 Object before the CF TTL has expired?CloudFront / S3 ETag:CloudFront 是否可以在 CF TTL 过期之前发送更新的 S3 对象? 【发布时间】:2021-08-22 23:40:47 【问题描述】:

我有一个关于 CloudFront 将如何使用 S3 对象的 ETag 来确定它是否需要发送刷新的对象的问题。

我知道 ETag 将成为 CloudFront 分配请求的一部分,在我的情况下,我看到的是“弱”(缩短)版本:

if-none-match: W/"eabcdef4036c3b4f8fbf1e8aa81502542"

如果发送的此 ETag 与 S3 对象的当前 ETag 值不匹配,则 CloudFront 将发送最新版本。

我看到这项工作按预期进行,但仅在达到 CloudFront 的缓存策略之后。在我的情况下,它被设置为 20 分钟。

具有缓存策略的 CloudFront

最小 TTL:1 最大 TTL:1200 默认 TTL:900 原始请求策略未设置

S3 存储桶

设置为仅允许通过其对应的 CloudFront 进行访问 以上分布。 存储桶和对象不公开 本例中的测试对象 (index.html) 只有一个标头集: 内容类型 = text/html 在使用 CloudFront 的缓存策略时,我还测试了 使用 Cache-Control = max-age=6000 的 S3 Object 标头 这对“index.html”对象的刷新没有影响 关于我要询问的 ETag 检查。

情景

在该 S3 存储桶的第一个“putObject”中,“index.html”文件的 ETag 为:

eabcdef4036c3b4f8fbf1e8aa81502542

当我点击该“index.html”文件的 URL (GET) 时,有效地启动了 20 分钟的缓存。

对“index.html”URL (GET) 的后续点击具有带有值的请求

if-none-match: W/"eabcdef4036c3b4f8fbf1e8aa81502542"

我还在返回的响应中看到“x-cache: Hit from cloudfront”。

在 20 分钟结束之前,我将更改“index.html”文件并通过代码中的“putObject”命令重新上传。

这会将 ETag 更改为:

exyzcde4099c3b4f8fuy1e8aa81501122

然后我希望 CloudFront 的下一个请求, 20 分钟 TTL 和旧的“if-none-match”值,然后会提示CloudFront 查看 ETag 不同并发送最新版本。

但在所有情况/测试中都没有。 CloudFront 似乎会忽略 ETag 差异并继续发送旧的“index.html”版本。

只有在 20 分钟(缓存 TTL)结束后,CloudFront 才会发送最新版本。

当时请求中的 ETag 也发生了变化/更新:

if-none-match: W/"exyzcde4099c3b4f8fuy1e8aa81501122" 

问题终于,嗯?):

有没有办法将 CloudFront 配置为侦听传入的 ETag,并在需要时发送最新的对象,而无需等待缓存策略 TTL 过期?

更新

Kevin Henry 的回答很好地说明了这一点:

"CloudFront 不知道您更新了 S3。您告诉它在 TTL 过期之前不要检查源。所以它只是提供旧文件,直到 TTL 过期并且它会看到新文件您上传到 S3 的。(请注意,这与 ETag 无关)。"

所以我决定测试如果我将 CloudFront 缓存策略对所有三个 CloudFront 设置都设置为 0 的 TTL 将如何使用 ETag。我知道这违背了 CloudFront 的目的和优势之一,但我仍在思考 CDN 缓存的某些关键方面。

将缓存设置为 0 后,我在返回的响应中看到持续的“来自 CloudFront 的未命中”。

我预料到了这一点,在第一个响应中我看到 HTTP 状态为 200。请注意,此测试返回的文件大小为 128KB。

对同一文件的后续调用会返回 304 的 HTTP 状态,返回的文件大小约为 400B。

当我更新 S3 存储桶中的“index.html”文件并调用相同的 URL 时,状态码为 200,文件大小为 128KB。

后续调用返回 304 状态,文件大小再次平均为 400B。

再看304的HTTP状态的定义:

https://httpstatuses.com/304

"已收到条件 GET 或 HEAD 请求,如果不是因为条件评估为 false 的事实,将导致 200 OK 响应。

换句话说,服务器不需要传输目标资源的表示,因为请求表明使请求有条件的客户端已经具有有效的表示;因此,服务器正在重定向客户端以使用该存储的表示,就好像它是 200 OK 响应的有效负载一样。"

那么我认为我此时正在使用浏览器的缓存是否正确?

对 CloudFront 的调用现在会将请求传递到 Origin,ETag 用于验证资源是否已更改。

如果没有,则返回 304,浏览器启动并返回其存储的“index.html”版本。

这是一个正确的假设吗?

如果您想知道,我不能使用失效方法来清除缓存,因为我的网站预计每天会发生数千次失效。我正在托管一个写作期刊网站,作者可以每天更新他们的文件,从而在 S3 上生成他们作品的新版本。

我也宁愿不使用版本控制方法,将时间戳或其他字符串作为查询添加到页面 URL。这主要是 SEO 的原因。

我的理想方案是提供作者作品的相同版本,直到他们更新它,此时对同一页面的下一次调用将显示其最新版本。

这项研究/练习帮助我学习和权衡我的选择。

再次感谢您的帮助/意见。

乔恩

【问题讨论】:

你能解释一下为什么你不能每天做数千次失效吗?你的意思是它太贵了?在任何情况下,通常处理动态内容的方式是使用较短的缓存时间(足够短,以至于您不介意内容过时的可能性),而改为依赖条件请求。您仍然需要网络请求来确认文件没有更改,但您不必重新传输实际数据,直到内容本身发生更改。 另一种方法是使用常量*** URL(缓存时间为 0),将实际内容作为子资源嵌入。然后,该子资源将结合长缓存时间使用文件名版本控制。这就是通常处理静态文件的方式。支持上述方法的动态内容不太常见。 @KevinChristopherHenry:是的,总成本是失效的因素。一开始我不会有太多,但是随着条目(和作者)数量的增加,成本会增加。就页面性能和新鲜内容而言,我的应用程序的动态方面得到了处理,这是我正在分析我的可能性的静态页面。至于将内容作为子资源嵌入,我认为您刚刚找到了我的下一个研究/测试领域。感谢您的帮助。 @KevinChristopherHenry:需要澄清一下(当我重新阅读我的评论时),当我说“静态”时,我的意思是一个预渲染的 HTML 文档,它不仅由JS/CSS 等依赖文件,但页面内容本身。想想静态站点生成器的输出。我正在分析我是否将在 CloudFront 上或通过其他基础设施提供这些 HTML 的“静态”页面。再次感谢。 @KevinChristopherHenry:非常正确。我现在正在重新审视 CDN 的想法,该 CDN 具有适用于各种页面类型的缓存策略,并在作者的条目更新时发出无效调用。我正在为此重新计算 CF 成本,并查看其他服务/解决方案。例如,CloudFlare 似乎不会为使 URL 失效而收费,并将其内置到他们的 API 中。也许是一个负载平衡的、基于 EC2 的 Varnish 解决方案。我仍在大量研究以找到我的“理想”,但很可能必须找到“理想”和“现实”之间的中间点。感谢您的意见,它有很大帮助。 【参考方案1】:

“我希望在 20 分钟 TTL 之前对 CloudFront 的下一个请求使用旧的 if-none-match 值,然后会提示 CloudFront 看到 ETag 是不同的,并且发送最新版本。”

这是一个错误的假设。 CloudFront 不知道您更新了 S3。您告诉它在 TTL 过期之前不要检查来源。所以它只是提供旧文件,直到 TTL 过期,它会看到您上传到 S3 的新文件。 (请注意,这与ETags 无关)。

CloudFront 确实提供了invalidate the cache 的方法,您可以在these answers 中阅读有关如何将其与 S3 更新结合起来的更多信息。

【讨论】:

很清楚。谢谢凯文。我已经通过关闭 CloudFront 缓存的各种实验更新了我的条目,并且正在查看如何使用 ETag。我看到第一次调用 CF 时返回 200 的 HTTP 状态,所有后续调用都返回 304。对同一个文件的更改将重置其 ETag 值,因此在下一次调用时会给出另一个状态 200,之后是 304。 CloudFront 所说的(用很多话来说)“没有任何变化,无需发送文件。使用浏览器的版本”,我是否正确? @JonDCruz:是的,服务器就是这样告诉客户端它的资源版本是最新的。请注意,这不是 CloudFront 特有的,它是一种通用的 HTTP 机制,称为 conditional requests。

以上是关于CloudFront / S3 ETag:CloudFront 是不是可以在 CF TTL 过期之前发送更新的 S3 对象?的主要内容,如果未能解决你的问题,请参考以下文章

亚马逊云端错误“拒绝访问”

在 CloudFront 的账户之间共享 S3 存储桶

S3 / Cloudfront 下载限制

Cloudfront:设置与 Origin 的 s3 路径差异

Cloudfront 域名无法使用 S3、Cloudfront 和 Route 53 将 HTTP 重定向到 HTTPS

Amazon S3 是不是向 CloudFront 发送失效信号?