Skip to content

Latest commit

 

History

History
63 lines (46 loc) · 7.2 KB

强缓存与协商缓存.md

File metadata and controls

63 lines (46 loc) · 7.2 KB

强缓存与协商缓存

浏览器缓存是浏览器在本地磁盘对用户最近请求过的资源进行存储,当访问者再次访问同一资源时,浏览器就可以直接从本地磁盘加载资源,通过缓存的方式就可以减少与服务器的数据传输,减少服务器的负担,加快页面响应速度等。

描述

良好的缓存策略可以降低资源的重复加载提高网页的整体加载速度,通常浏览器缓存策略分为强缓存和协商缓存。常见的HTTP缓存只能存储GET响应,对于其他类型的响应则不会进行缓存。
理论上来讲,当一个资源被缓存存储后,该资源应该可以被永久存储在缓存中,由于缓存只有有限的空间用于存储资源副本,所以缓存会定期地将一些副本删除,这个过程叫做缓存驱逐。另一方面,当服务器上面的资源进行了更新,那么缓存中的对应资源也应该被更新,由于HTTPC/S模式的协议,服务器更新一个资源时,不可能直接通知客户端更新缓存,所以双方必须为该资源约定一个过期时间,在该过期时间之前,该资源的缓存副本就是新鲜的,当过了过期时间后,该资源的缓存副本则变为陈旧的。驱逐算法用于将陈旧的资源缓存副本替换为新鲜的,注意,一个陈旧的资源缓存副本是不会直接被清除或忽略的,当客户端发起一个请求时,缓存检索到已有一个对应的陈旧资源缓存副本,则缓存会先将此请求附加一个If-None-Match头,然后发给目标服务器,以此来检查该资源副本是否是依然还是算新鲜的,若服务器返回了304 (Not Modified),则表示此资源副本是新鲜的,注意该响应不会有带有实体信息,通过这种方式,可以节省一些带宽。若服务器通过If-None-MatchIf-Modified-Since判断后发现已过期,那么会带有该资源的实体内容返回。对上面的请求过程可以概括如下:

  • 浏览器在发起对于资源的请求时,会首先检查本地是否存在缓存,如果存在缓存则通过expirescache-control检查缓存是否过期,如果命中缓存且缓存未过期,则直接使用本地缓存。
  • 本地缓存未命中,则浏览器向服务器发送一个协商请求,通过last-modifiedetag验证资源是否命中协商缓存,如果命中则服务器会将这个请求响应为304,但是不会返回这个资源的数据,依然是从缓存中读取资源,如果未命中则会携带资源返回且响应为200

强缓存

强缓存是通过ExpiresCache-Control来控制缓存在本地的有效期。

Expires

ExpiresHTTP 1.0提出的一个表示资源过期时间的Header,它描述的是一个绝对时间,由服务器返回。Expires受限于本地时间,如果修改了本地时间,可能会造成缓存失效.对于资源的请求,如果在Expires之内,则浏览器会直接读取缓存,不再请求服务器。

Expires: Sun, 14 Jun 2020 02:50:57 GMT

Cache-Control

Cache-Control出现于HTTP 1.1,优先级高于Expires,表示的是相对时间,请求头和响应头都支持这个属性,通过它提供的不同的值来定义缓存策略。

Cache-Control: max-age=300
  • Cache-Control: no-store: 缓存中不得存储任何关于客户端请求和服务端响应的内容,每次由客户端发起的请求都会下载完整的响应内容。
  • Cache-Control: no-cache: 缓存中会存储服务端响应的内容,只是在与服务端进行新鲜度再验证之前,该缓存不能够提供给浏览器使用。简单来说,就是浏览器会将服务端响应的资源进行缓存,但是在每次请求时,缓存都要向服务端评估缓存响应的有效性,协商缓存是否可用,根据响应是304还是200判断是使用本地缓存资源还是使用服务器响应的资源。
  • Cache-Control: public || private: public表示该响应可以被任何中间人比如中间代理、CDN等缓存。默认响应为privateprivate表示该响应是专用的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。
  • Cache-Control: max-age=31536000: 响应为最大的过期时间,其指令是max-age=<seconds>,表示资源能够被缓存即保持新鲜的最大时间,max-age是距离请求发起的时间的秒数。
  • Cache-Control: must-revalidate: 当使用了must-revalidate指令,那就意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。在正常情况下是没有必要使用这个指令的,因为在强缓存过期的情况下会进行协商缓存,但是HTTP规范是允许客户端在某些特殊情况下直接使用过期缓存的,比如校验请求发送失败的时候,还比如有配置一些特殊指令stale-while-revalidatestale-if-error等的时候,must-revalidate指令就是让缓存在过期后的任何情况下都必须重新验证。

协商缓存

当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的HTTP状态为304 (Not Modified),该请求不携带实体数据,若未命中,则返回200并携带资源实体数据。协商缓存是利用的是Last-Modified,If-Modified-SinceETag、If-None-Match这两对Header来管理的。

Last-Modified If-Modified-Since

Last-Modified,If-Modified-SinceHTTP 1.0引入的,Last-Modified表示本地文件最后修改日期,浏览器会在请求头加上If-Modified-Since即上次响应的Last-Modified的值,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,但是如果在本地打开缓存文件,就会造成Last-Modified被修改,所以在HTTP 1.1出现了ETag

ETag If-None-Match

Etag就像一个指纹,资源变化都会导致ETag变化,跟最后修改时间没有关系,ETag可以保证每一个资源是唯一的,If-None-Match的请求头字段会将上次返回的Etag发送给服务器,询问该资源的Etag是否有更新,有变动就会发送新的资源回来。ETag的优先级比Last-Modified更高,具体使用ETag主要出于下面几种情况考虑:

  • 一些文件也许会周期性的更改,但是他的内容并不改变,比如仅仅改变的修改时间,这个时候我们并不希望客户端认为这个文件被修改了,而重新GET
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,例如1s内修改了N次,If-Modified-Since能检查到的粒度是秒级的,这种修改无法判断。
  • 某些服务器不能精确的得到文件的最后修改时间。

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://zhuanlan.zhihu.com/p/60357719
https://www.jianshu.com/p/9c95db596df5
https://segmentfault.com/a/1190000008956069
https://github.com/amandakelake/blog/issues/41
https://juejin.im/post/5ccfccaff265da03ab233bf5
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ