【发布时间】: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状态的定义:
"已收到条件 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 解决方案。我仍在大量研究以找到我的“理想”,但很可能必须找到“理想”和“现实”之间的中间点。感谢您的意见,它有很大帮助。
标签: caching amazon-cloudfront etag ttl