如何生成和配置 ETag?
How ETags are generated and configured?
我最近通过了 ETag
HTTP header 的概念。 (this) 但我仍然有一个问题,对于负责生成 ETags 的特定 HTTP 资源?
换句话说就是实际应用、容器(Ex:Tomcat)、Web Server/Load balancer(Ex:Apache/Nginx)?
有人可以帮忙吗?
与 HTTP 规范的大多数方面一样,责任最终落在提供资源的人身上。
当然,在这种情况下,我们通常会使用工具——服务器、负载平衡器、应用程序框架等——来帮助我们履行这些职责。但是没有任何规范定义与应用程序相对的“网络服务器”应该提供什么,这只是一个实际问题,即您正在使用的工具中有哪些功能可用。
现在,特别是 ETags
,一种常见的情况是框架或 Web 服务器可以配置为自动散列响应(正文或其他内容)并将结果放入 ETag
。然后,根据条件请求,它将生成一个响应并对其进行散列以查看它是否已更改,如果没有更改则自动发送条件响应。
举两个我熟悉的例子,nginx can do this with static files at web server level, and Django can do this 在应用程序级别具有动态响应。
这种方法很常见,易于配置,而且效果很好。但是,在某些情况下,它可能不是最适合您的用例。例如:
- 要计算哈希以与传入的
ETag
进行比较,您首先必须有一个响应。因此,虽然条件响应可以为您节省传输响应的开销,但它不能为您节省生成响应的成本。因此,如果生成响应的成本很高,并且您有 ETags
的替代来源(例如,存储在数据库中的版本号),则可以使用它来获得更好的性能。
- 如果您打算使用
ETags
到 prevent accidental overwrites with state-changing methods,您可能需要添加自己的应用程序代码以使比较和设置逻辑原子化。
因此在某些情况下,您可能希望在应用程序级别创建 ETags
。再次以 Django 为例,它提供了一种简单的方法让你 provide your own function 计算 ETags
.
总而言之,为您控制的资源提供 ETags
最终是您的责任,但您也可以利用软件堆栈中的工具为您完成。
Web 服务器中使用的典型算法概述。
假设我们有一个包含
的文件
- 大小 1047,即十六进制的 417。
- MTime 即最后一次 mod 化于 2020 年 1 月 6 日星期一 12:54:56 GMT
在 Unix 时间中是 1578315296 秒或 1578315296666771000 纳秒。
- Inode 是一个物理文件号 66,即十六进制的 42
不同的网络服务器returns 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 无论如何都会以纳米为单位
- 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()
- 基图拉 (Swift):
"W/hex(Size)-hex(MTime)"
StaticFileServer.calculateETag
一些想法:
- 这里经常使用十六进制数,因为将十进制数转换为较短的十六进制字符串的成本较低。
- 如果您在应用程序重新部署期间简单地复制文件,则 Inode 在添加更多保证的同时会使负载平衡变得不可能并且非常脆弱。
MTime
纳秒级并非在所有平台上都可用,不需要这种粒度。
- Apache 有一个类似 https://bz.apache.org/bugzilla/show_bug.cgi?id=55573
的错误
MTime-Size
或 Size-MTime
的顺序也很重要,因为 MTime
更有可能发生变化,因此比较 ETag 字符串可能会更快一些 CPU 个周期。
- 即使这不是完整的校验和哈希,但绝对不是弱 ETag。这足以表明我们期望 Range 请求的八进制兼容性。
- Apache 和 Nginx 共享几乎所有互联网流量,但大多数静态文件通过 Nginx 共享并且不可配置。
看起来 Nginx 使用了最合理的架构,因此如果您实施,请尝试使其相同。
一行C语言生成的整个ETag:
printf("\"%" PRIx64 "-%" PRIx64 "\"", last_mod, file_size)
我的提议是采用 Nginx 模式并将其作为 recommended ETag algorithm by W3C。
我最近通过了 ETag
HTTP header 的概念。 (this) 但我仍然有一个问题,对于负责生成 ETags 的特定 HTTP 资源?
换句话说就是实际应用、容器(Ex:Tomcat)、Web Server/Load balancer(Ex:Apache/Nginx)?
有人可以帮忙吗?
与 HTTP 规范的大多数方面一样,责任最终落在提供资源的人身上。
当然,在这种情况下,我们通常会使用工具——服务器、负载平衡器、应用程序框架等——来帮助我们履行这些职责。但是没有任何规范定义与应用程序相对的“网络服务器”应该提供什么,这只是一个实际问题,即您正在使用的工具中有哪些功能可用。
现在,特别是 ETags
,一种常见的情况是框架或 Web 服务器可以配置为自动散列响应(正文或其他内容)并将结果放入 ETag
。然后,根据条件请求,它将生成一个响应并对其进行散列以查看它是否已更改,如果没有更改则自动发送条件响应。
举两个我熟悉的例子,nginx can do this with static files at web server level, and Django can do this 在应用程序级别具有动态响应。
这种方法很常见,易于配置,而且效果很好。但是,在某些情况下,它可能不是最适合您的用例。例如:
- 要计算哈希以与传入的
ETag
进行比较,您首先必须有一个响应。因此,虽然条件响应可以为您节省传输响应的开销,但它不能为您节省生成响应的成本。因此,如果生成响应的成本很高,并且您有ETags
的替代来源(例如,存储在数据库中的版本号),则可以使用它来获得更好的性能。 - 如果您打算使用
ETags
到 prevent accidental overwrites with state-changing methods,您可能需要添加自己的应用程序代码以使比较和设置逻辑原子化。
因此在某些情况下,您可能希望在应用程序级别创建 ETags
。再次以 Django 为例,它提供了一种简单的方法让你 provide your own function 计算 ETags
.
总而言之,为您控制的资源提供 ETags
最终是您的责任,但您也可以利用软件堆栈中的工具为您完成。
Web 服务器中使用的典型算法概述。 假设我们有一个包含
的文件- 大小 1047,即十六进制的 417。
- MTime 即最后一次 mod 化于 2020 年 1 月 6 日星期一 12:54:56 GMT 在 Unix 时间中是 1578315296 秒或 1578315296666771000 纳秒。
- Inode 是一个物理文件号 66,即十六进制的 42
不同的网络服务器returns 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 无论如何都会以纳米为单位 - 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()
- 基图拉 (Swift):
"W/hex(Size)-hex(MTime)"
StaticFileServer.calculateETag
一些想法:
- 这里经常使用十六进制数,因为将十进制数转换为较短的十六进制字符串的成本较低。
- 如果您在应用程序重新部署期间简单地复制文件,则 Inode 在添加更多保证的同时会使负载平衡变得不可能并且非常脆弱。
MTime
纳秒级并非在所有平台上都可用,不需要这种粒度。 - Apache 有一个类似 https://bz.apache.org/bugzilla/show_bug.cgi?id=55573 的错误
MTime-Size
或Size-MTime
的顺序也很重要,因为MTime
更有可能发生变化,因此比较 ETag 字符串可能会更快一些 CPU 个周期。- 即使这不是完整的校验和哈希,但绝对不是弱 ETag。这足以表明我们期望 Range 请求的八进制兼容性。
- Apache 和 Nginx 共享几乎所有互联网流量,但大多数静态文件通过 Nginx 共享并且不可配置。
看起来 Nginx 使用了最合理的架构,因此如果您实施,请尝试使其相同。 一行C语言生成的整个ETag:
printf("\"%" PRIx64 "-%" PRIx64 "\"", last_mod, file_size)
我的提议是采用 Nginx 模式并将其作为 recommended ETag algorithm by W3C。