FrankKai / FrankKai.github.io

FE blog

Home Page:https://frankkai.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

HTTP缓存之ETag

FrankKai opened this issue · comments

  • 初识ETag
    • ETag是req header还是res header?
    • ETag的作用是什么?(标记资源version)
    • 为什么要用对资源标记版本?
    • ETag还解决了什么问题?
    • 如何生成一个ETag?
    • ETag和什么已知的知识点类似?
    • ETag可以做持久化吗?
    • ETag是响应头还是请求头?
  • ETag语法
  • ETag指令
  • 避免mid-air相撞
  • 缓存未更改的资源(304 Not Modified是怎么回事?)
  • 项目中的ETag分析(强验证,弱验证)

初识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可以缓存。

"<etag_value>" (核心)

  • 实体的tag唯一代表请求的资源。
  • etag由双引号扩起来的ASCII字符组成,例如:”675af34563dc-tr34“,"5615195CC81CB676D5EB5224558EB0EC"。
  • etag生成的方法是不能直接指定的。
  • etag通常由content hash,last modification timestamp hash或者直接一个版本号组成。例如mdn的etag由一个代表wiki文章内容的十六进制hash组成:etag: "5e7468cd-7de"

避免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