FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

HTTP缓存之ETag #239

Open FrankKai opened 3 years ago

FrankKai commented 3 years ago

初识ETag

ETag是req header还是res header?

ETag是HTTP response header。

ETag的作用是什么?

标记资源version。 资源版本的唯一标识。 可以理解成一个唯一的hash。类似唯一标识或者版本标识。 类似uuid,primary key,package.json version,git commit hash,git tag等等。 目前接触过的唯一标识有uuid,数据库primary key等等。 目前接触过的版本号有package.json的version,commit hash等等。 目前接触过的tag有git的tag。

为什么要用对资源标记版本?

对资源标记版本的话,可以使得缓存的使用更加高效而且节省带宽。 因为如果content没有发生变化的话,server是没有必要重新发送一个完整的response到client的。

ETag还解决了什么问题?

ETag可以阻止同时更新一个资源出现的覆盖问题。这个问题叫做“mid-air collisions”。

如何生成一个ETag?

如果资源的URL发生了变化,必须生成一个新的ETag。 对比新旧资源的ETag可以判断出两个资源是不是相同的。

ETag和什么已知的知识点类似?

类似uuid,primary key,package.json version,git commit hash,git tag等等。 说得通俗易懂一些的话,与指纹类似,一些服务器可以通过ETag做到追踪。

ETag可以做持久化吗?

可以。 ETag可以设置一个持久化的值,从而服务器可以永久追踪到,也算是一种数据持久化。

ETag是响应头还是请求头?

响应头。 ETag不是一个forbidden header name。

ETag语法

ETag: W/"<etag_value>"
ETag: "<etag_value>"

ETag指令

W/ (Optional)

'W/'(大小写敏感) 的意思是weak validator(弱验证)。 弱etag是很容易生成的,但是对于比较来说就不是很有用了。 强验证是最理想化的比较情况,但是很难高效率的生成。 同一资源的两种表现形式的弱ETag的值在语义上可能是等价的,但是并不是byte级相等。 weak etag在byte range requests使用时,阻止缓存;但是strong etag意味着range requests可以缓存。

"" (核心)

避免mid-air相撞(ETag和If-Match)

ETag与If-Match头一起作用时,可以检测到mid-air(空中)编辑冲突。 例如,在编辑MDN时,当前的wiki content被哈希化,之后在response的ETag中传入:

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

当向wiki page存储内容时(比如发送数据),POST请求将在If-Match头中包含上ETag的值,去检查这个内容是不是fresh的:

If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

如果hash没有匹配到,意味着文档在中间被编辑,然后抛出一个412状态码代表条件失败错误。

缓存未更改的资源

ETag头的另一个典型用处就是:缓存没有发生变化的资源。 如果用户再次访问了一个给定的URL(设置了ETag的资源),而且它是stale的(旧的:过于老导致不能使用),client会发送ETag的值在If-None-Match header上:

If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

服务端回对比cleint通过If-None-Match发送过来的ETag,并且与当前版本的资源对比。 如果两个值比对上了(说明资源没有变化),server会发送一个304 Not Modified状态码,并且没有body,会告诉client:”response的缓存版本当前是可用的。(fresh)“

项目中的ETag分析

304

dist目录中的vender.xxx.js,位于云存储上。

// req header
if-none-match: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"
// res header
etag: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"

200

dist目录中的app.xxx.js,位于云存储上。

// req header 
Provisional headers are shown
// res header
etag: "FumFsXBIhM_vEqYsfLGotbfshnIdxRcVfs8.gz"

disable cache与ETag

强验证。 关闭disable时:304。

// req header
if-none-match: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"
// res header
etag: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"

弱验证。 开启disable时:200。

// req header 没有if-none-match
cache-contorl: no-cache
// res header
etag: W/"sfFkYsIbsfafvg0crbkQpRWyzDWRPxFC9C"

参考资料:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag