Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Q111】http 响应头中的 ETag 值是如何生成的 #112

Open
shfshanyue opened this issue Dec 10, 2019 · 3 comments
Open

【Q111】http 响应头中的 ETag 值是如何生成的 #112

shfshanyue opened this issue Dec 10, 2019 · 3 comments
Labels

Comments

@shfshanyue
Copy link
Owner

No description provided.

@shfshanyue
Copy link
Owner Author

shfshanyue commented Dec 10, 2019

关于 etag 的生成需要满足几个条件,至少是宽松满足

  1. 当文件更改时,etag 值必须改变。
  2. 尽量便于计算,不会特别耗 CPU。这样子利用摘要算法生成 (MD5, SHA128, SHA256) 需要慎重考虑,因为他们是 CPU 密集型运算
  3. 必须横向扩展,分布式部署时多个服务器节点上生成的 etag 值保持一致。这样子 inode 就排除了

关于服务器中 etag 如何生成可以参考 HTTP: Generating ETag Header

以上几个条件是理论上的成立条件,那在真正实践中,应该如何处理?

我们来看一下 nginx 中是如何做的

nginx 中 ETag 的生成

我翻阅了 nginx 的源代码,并翻译成伪代码如下:由 last_modified 与 content_length 拼接而成

etag = header.last_modified + header.content_lenth

可见源码位置,并在以下贴出: ngx_http_core_modules.c

etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
                                  r->headers_out.last_modified_time,
                                  r->headers_out.content_length_n)
                      - etag->value.data;

总结:nginxetag 由响应头的 Last-ModifiedContent-Length 表示为十六进制组合而成。

随手在我的k8s集群里找个 nginx 服务测试一下

$ curl --head 10.97.109.49
HTTP/1.1 200 OK
Server: nginx/1.16.0
Date: Tue, 10 Dec 2019 06:45:24 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 23 Apr 2019 10:18:21 GMT
Connection: keep-alive
ETag: "5cbee66d-264"
Accept-Ranges: bytes

etag 计算 Last-ModifiedContent-Length,使用 js 计算如下,结果相符

> new Date(parseInt('5cbee66d', 16) * 1000).toJSON()
"2019-04-23T10:18:21.000Z"
> parseInt('264', 16)
612

Nginx 中的 ETag 算法及其不足

协商缓存用来计算资源是否返回 304,我们知道协商缓存有两种方式

  • Last-Modified/if-Modified-Since
  • ETag/If-None-Match

既然在 nginxETagLast-ModifiedContent-Length 组成,那它便算是一个加强版的 Last-Modified 了,那加强在什么地方呢?

Last-Modified 是由一个 unix timestamp 表示,则意味着它只能作用于秒级的改变,而 nginx 中的 ETag 添加了文件大小的附加条件

那下一个问题:如果 http 响应头中 ETag 值改变了,是否意味着文件内容一定已经更改

答案:不能。

因此使用 nginx 计算 304 有一定局限性:在 1s 内修改了文件并且保持文件大小不变。但这种情况出现的概率极低就是了,因此在正常情况下可以容忍一个不太完美但是高效的算法。

相关问题

@jacintoface
Copy link

Last-Modified 变了,但是Content-Length没变(文件内容不变),是否意味着etag的缓存失效

@shfshanyue
Copy link
Owner Author

@jacintoface 也可以这么理解

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants