【问题标题】:How ETags are generated and configured?ETag 是如何生成和配置的?
【发布时间】:2018-05-10 18:10:27
【问题描述】:

我最近了解了ETag HTTP 标头的概念。 (this) 但是我仍然有一个问题,对于负责生成 ETag 的特定 HTTP 资源?

换句话说,它是实际的应用程序、容器(例如:Tomcat)、Web 服务器/负载均衡器(例如:Apache/Nginx)?

有人可以帮忙吗?

【问题讨论】:

    标签: apache rest tomcat nginx etag


    【解决方案1】:

    与 HTTP 规范的大多数方面一样,责任最终在于提供资源的人。

    当然,我们经常使用工具(服务器、负载平衡器、应用程序框架等)来帮助我们履行这些职责。但是没有任何规范来定义“Web 服务器”(与应用程序相反)应该提供什么,这只是一个实际问题,即您使用的工具中有哪些功能可用。

    现在,特别是查看ETags,一个常见的情况是框架或 Web 服务器可以配置为自动散列响应(正文或​​其他内容)并将结果放入ETag。然后,在有条件的请求上,它会生成一个响应并对其进行哈希处理以查看它是否已更改,如果没有更改,则自动发送条件响应。

    举两个我熟悉的例子,nginx can do this 在 Web 服务器级别使用静态文件,Django can do this 在应用程序级别使用动态响应。

    这种方法很常见,易于配置,而且效果很好。但是,在某些情况下,它可能不是最适合您的用例。例如:

    • 要计算哈希值以与传入的ETag 进行比较,您首先必须得到响应。因此,尽管条件响应可以为您节省传输响应的开销,但它不能为您节省生成响应的成本。因此,如果生成响应的成本很高,并且您有 ETags 的替代来源(例如,存储在数据库中的版本号),您可以使用它来获得更好的性能。
    • 如果您打算使用ETagsprevent accidental overwrites with state-changing methods,您可能需要添加自己的应用程序代码以使您的比较和设置逻辑具有原子性。

    因此,在某些情况下,您可能希望在应用程序级别创建 ETags。再次以 Django 为例,它为您提供了一种简单的方法,可以通过provide your own function 计算ETags

    总而言之,为您控制的资源提供ETags 最终是您的责任,但您也可以利用软件堆栈中的工具为您完成这项工作。

    【讨论】:

    • 你的回答很好。让我验证这些事情。因此,正如您所说,我们可以(通常)配置一个 Web 服务器来执行 etag 业务(第 3 段)以及一个应用程序框架(第 4 段)。根据您的理解,两种方法的性能是否存在潜在差异?还是可以忽略不计?请将此添加到答案中:))
    • @SupunWijerathne:我添加了一些具体原因,说明默认的基于哈希的方法可能并不总是合适的。不可能一概而论哪个更好,但我通常建议您使用默认的基于哈希的方式,除非您自己提出了令人信服的理由。
    • 只是另一个问题,在某些情况下我们必须手动计算特定资源的最后修改时间,这可能不太合适,对吧?
    • @SupunWijerathne:恐怕我不明白你在问什么。使用 Last-Modified 标头是另一种情况,因此您最好发布一个单独的问题。
    【解决方案2】:

    网络服务器中使用的典型算法概述。 考虑我们有一个文件

    • 大小为 1047,即 417(十六进制)。
    • MTime 即最后一次修改时间为 2020 年 1 月 6 日星期一 12:54:56 GMT 在 unix 时间中是 1578315296 秒或 1578315296666771000 纳秒。
    • inode 是物理文件编号 66,即十六进制的 42

    不同的网络服务器返回 ETag 如下:

    • Nginx:"5e132e20-417""hex(MTime)-hex(Size)"。不可配置。
    • BusyBox httpd 和 Nginx 一样
    • monkey httpd和Nginx一样
    • Apache/2.2:"42-417-59b782a99f493",即"hex(INode)-hex(Size)-hex(MTime in nanoseconds)"。可以是 configured,但 MTime 无论如何都会是 nanos
    • Apache/2.4:"417-59b782a99f493""hex(Size)-hex(MTime in nanoseconds)" 即没有 INode,当相同文件在不同服务器上具有不同 INode 时,这对负载平衡很友好。
    • OpenWrt uhttpd:"42-417-5e132e20",即"hex(INode)-hex(Size)-hex(MTime)"。不可配置。
    • Tomcat 9:W/"1047-1578315296666",即Weak"Size-MTime in milliseconds"。这是incorrect ETag,因为它应该像静态文件一样强大,即八进制兼容性。
    • LightHTTPD:"hashcode(42-1047-1578315296666771000)"INode-Size-MTime,但随后通过哈希码 (dekhash) 简化为简单整数。可以配置,但只能禁用一部分 (etag.use-inode = "disabled")
    • MS IIS:它的格式为Filetimestamp:ChangeNumber,例如“53dbd5819f62d61:0”。未记录,不可配置,但可以禁用。
    • Jetty:基于上一个 mod、大小和散列。见Resource.getWeakETag()
    • 奇图拉(斯威夫特):"W/hex(Size)-hex(MTime)"StaticFileServer.calculateETag

    一些想法:

    • 这里经常使用十六进制数字,因为将十进制数字转换为较短的十六进制字符串很便宜。
    • 如果您在应用程序重新部署期间简单地复制文件,Inode 虽然添加更多保证会使负载平衡变得不可能并且非常脆弱。 MTime(以纳秒为单位)并非在所有平台上都可用,因此不需要这种粒度。
    • Apache 有一个关于此的错误,例如 https://bz.apache.org/bugzilla/show_bug.cgi?id=55573
    • MTime-SizeSize-MTime 的顺序也很重要,因为 MTime 更可能发生更改,因此在十几个 CPU 周期内比较 ETag 字符串可能会更快。
    • 即使这不是一个完整的校验和哈希但绝对不是一个弱 ETag。这足以表明我们期望 Range 请求具有八进制兼容性。
    • Apache 和 Nginx 几乎共享 Internet 上的所有流量,但大多数静态文件是通过 Nginx 共享的,并且不可配置。

    看起来 Nginx 使用了最合理的模式,所以如果你实现尝试使其相同。 用 C 语言生成的整个 ETag 一行:

    printf("\"%" PRIx64 "-%" PRIx64 "\"", last_mod, file_size)
    

    我的建议是采用 Nginx 架构并将其设置为 recommended ETag algorithm by W3C

    【讨论】:

    • 这篇迟到的帖子的各个部分(发布到一个 3 年以上的主题,并且答案已经被接受)在redmine.lighttpd.net/issues/3055 受到了挑战
    猜你喜欢
    • 2020-10-14
    • 2014-08-24
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    • 2013-06-05
    • 2015-08-01
    相关资源
    最近更新 更多