正确使用ETags

50

我正在阅读一本书,对于ETag这一章节有一个特定的问题。作者说ETags可能会影响性能,并且你必须精细调整或完全禁用它们。

我已经知道ETags是什么并理解风险,但是难道正确使用ETags很困难吗?

我刚刚做了一个应用程序,发送的ETag值是响应体的MD5哈希值。 这是一个简单的解决方案,在许多语言中都很容易实现。

  • 将响应体的MD5哈希作为ETag使用是否错误? 如果是,为什么?

  • 为什么作者(显然比我聪明得多)没有提出这样一个简单的解决方案?

除非你是作者 :), 否则这个问题很难回答,因此我正在尝试找到使用MD5哈希作为ETag的弱点。


10
假定作者比你聪明一丁点是绝对不安全的,切记。 - spender
23
同意,但假设你比作者更聪明甚至更不安全。 - Oddthinking
16
我们甚至不会触及到网络评论员的智慧,无论是哪种方式;-) - Will Hartung
2
你生成了“响应主体的MD5哈希值”,这意味着你的服务器必须再次生成相同的响应来计算哈希值!? 如果是这样,你节省了数据传输量,但并没有减轻服务器负载。 - Kambiz
@Kambiz 即使在服务器负载方面也可以节省,当第一次计算完成后,该值得到缓存,您知道哪些事件可能使其失效并可以订阅所有这些事件。这可能会变得复杂... - maaartinus
4个回答

55

ETag与Last-Modified头类似。它是一种由客户端确定更改的机制。

ETag需要是代表资源状态和特定格式的唯一值(一个资源可能有多个格式,每个格式都需要自己的ETag)。不是整个资源域中唯一的,只是在该资源内部唯一。

从技术上讲,相对于Last-Modified头,ETag具有“无限”分辨率。Last-Modified仅以1秒的粒度更改,而ETag可以是亚秒级别的。

您可以实现ETag和Last-Modified两者,也可以只使用其中之一(或两者都不使用)。如果Last-Modified不够用,则考虑使用ETag。

请注意,我不会为“每个”资源设置ETag。基本上,我不会为任何没有被缓存期望的东西设置它(显然是动态内容)。在这种情况下是没有意义的,只是浪费了工作。

编辑:我看到了你的编辑,并进行了澄清。

MD5很好。唯一的缺点是需要一直计算MD5。对于200K大小的PDF文件,运行MD5非常昂贵。对于没有被缓存期望的资源运行MD5是完全浪费的(即动态内容)。

关键在于无论使用什么机制,它都应该像Last-Modified通常那样便宜。再次强调,Last-Modified通常是资源的属性,并且通常非常便宜。

ETag应该是类似便宜的。如果您使用MD5,并且可以缓存/存储资源和MD5哈希之间的关联,则这是一个不错的解决方案。但是,每次需要ETag时重新计算MD5,基本上与使用ETag来提高整体服务器性能的想法相反。


1
谢谢。在我的特定情况下,我已经拥有了MD5,因为我正在数字签名请求,但我看到这可能是其他场景的性能问题。谢谢! - Pablo Fernandez
19
“ETag只是恰好等于上次修改日期(即相同的文本),符合ETag的所有必要标准。”这是不正确的,因为经过Gzip压缩和未经压缩的响应将具有相同的修改日期;然而它们应该具有不同的ETag:https://issues.apache.org/bugzilla/show_bug.cgi?id=39727 - johnstok
17
在现代处理器上,对于一个200K的PDF文件执行MD5操作通常不会很费时 - 甚至比Gbit以太网快得多,更不用说终端用户的互联网连接速度了。如果您的ETag有可能避免1%的传输,那么使用CPU时间也许就是值得的。 - intgr
@Will 我编辑了你的答案,使其更加准确,如果你认为还不够优化,请随意进一步编辑。 - Félix Adriyel Gagnon-Grenier

9
我们在Instela中使用ETags来处理动态内容。
我们的策略是在输出生成内容的MD5哈希值发送时,如果存在if-none-match标头,则将标头与生成的哈希值进行比较。如果两个值相同,则发送304代码并中断请求,而不返回任何内容。
尽管我们需要消耗一些CPU来计算哈希值,但最终我们可以节省大量的带宽。
我们有一个Facebook新闻源样式的主页,每个用户都有不同的内容。由于新闻提要内容每小时只更改3-4次,因此对于客户端来说,主页面刷新非常高效。在移动设备时代,我认为花费更多的CPU时间比花费带宽更好。带宽仍然比CPU更昂贵,并且这对客户端来说是更好的体验。

2
没有阅读这本书,我无法谈论作者确切的担忧。
然而,生成ETags应该是这样的:只有在页面更改时才会生成ETag。对网页生成MD5哈希会消耗服务器的处理能力和时间;如果有很多客户端连接,它可能会开始引起性能问题。
因此,您需要一种好的技术,在必要时仅生成ETags,并将其缓存于服务器上,直到相关页面发生更改。

1
我必须使用共享密钥对每个服务器响应进行数字签名。因此,ETag是一个不错的副作用 :) - Pablo Fernandez

1

我认为ETAGS的“感知问题”可能是您的浏览器需要对页面上的每个资源发出并解析(简单而小)的请求/响应,以检查ETAG值是否已在服务器端更改。

我个人认为,对于经常更改的图像、CSS、JavaScript(如果浏览器的ETAG当前,则服务器无需重新发送内容),这些额外的小往返到服务器是可以接受的,因为该机制使得标记“已更新”的内容非常容易。


书中提到的问题是,您需要想出一种特殊且可能聪明的策略(作者甚至鼓励如果找不到好策略,可以放弃对Etags的支持)。这就是我觉得奇怪的地方,MD5是一个好的解决方案吗?如果是,为什么不直接说呢? - Pablo Fernandez
@Pablo Fernandez:MD5还可以,但我个人不会对整个文件内容进行哈希。哈希“最后文件修改日期”应该足够了。关于“为什么不直接这么说呢?”:答案可能就在书名(《高性能网站》)中。Etag(及其往返)确实增加了一些开销,并且可能是考虑到重载的Web服务器上需要考虑的一个重要因素(但同时它们也增加了灵活性)... - ChristopheD
1
@Nicolás:没错,但是 max-age 或者 expires 不能保证客户端总是接收到最新的内容。 - ChristopheD
1
哈希修改日期是无用的。如果你要这样做,那么你可能会放弃ETags并让客户端使用Last-Modified + If-Modified-Since。ETags的整个意义在于它们具有比1秒更好的分辨率,并且可以“返回”先前发送的ETag。 - Nicolás
@Nicolás:非常正确(有道理)。最后修改时间/如果修改自上次请求以来的组合将表现几乎相同,表示最近更改的时间戳(它们可能更适合此工作;-)。 - ChristopheD
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接