0


简析强制缓存和协商缓存

零、目录

  1. 背景介绍
  2. http 缓存机制
  3. 使用小结

一、 背景介绍

    浏览器和服务器进行交互的过程, 时间开销的瓶颈往往出现在数据的传输的过程之中。

    这个场景类似介于 **A城** 到 **B城** 之间只有一座 “通道” , 每次想从**A城** 到 **B城** ,必须按照人数交付高昂的路费, 那么如果要减少这种高昂的路费开销的话, 核心思想就是**尽可能的减少通过这座 “通道” 的次数**又或者**减少通过这座通道的“人数”**。基于这种理念,在 http协议的基础上, 提出了一种**协议缓存**, 这种**协议缓存**又可以细分为 **强制缓存** 和 **协商缓存** 两种,分别对应上述**减少过桥次数**和**减少过桥人数**的理念。

二、http缓存机制简介

    **1. 强制缓存**

    **强制缓存**的思想是,在浏览器内置数据库中缓存每次请求中 “**可以被缓存**” (受到一些关键字的管控)的静态资源如 *image, css, js* 文件, 当第二次请求被缓存过的资源时候,会通过校验两个字段 ***Expires*** 和 ***Cache-Control*** 的***max-age***字段(注意,***Expires ***是 ***http1.0*** 的产物, ***Cache-Control ***则是 ***http1.1*** 的产物。 两者同时存在, 或者只存在其中之一, 都可以触发**强制缓存**)
  • 当满足字段约束的情况下, 浏览器就不会向服务器发送请求而是直接从服务器返回数据, 同时其状态码为 200
  • 当不满足字段约束的情况下, 浏览器则会向服务器正常发送请求, 具体流程可见图(01.强制缓存示意图)

                   (01.强制缓存示意图)    

    **强制缓存**主要取决于两个字段 ***Expires*** 和 ***Cache-Control ***中的 ***max-age ***字段, 在两个响应头都存在的情况下, 其流程如图 (02. 强制缓存执行流程图)所示 

(02. 强制缓存执行流程图)

    如图(02. 强制缓存执行流程图)可知, 当两个字段同时存在得到时候, ***Cache-Control*** 中的 ***max-age ***字段字段优先级会稍微高一点, 当 ***Cache-Control*** 中的 ***max-age ***字段校验成功,会直接返回浏览器内置数据库的缓存, 失效时才会将决策权传递给 ***Expires ***字段判断。

    这样设计的原因,大概是因为 ***Expires ***字段在设计时存在了这么一个缺陷——***Expires***字段返回的是**服务器的时间**, 而非**客户端的本机时间**。 当存在时差, 或者客户修改本地时间的情况下 ***Expires*** 字段会存在失效的可能性,比如 当**同一时刻下**的**服务器时间**为 2022/4/26 06:00:00 **客户端时间**为 2022/4/26 12:00:00 过期时间为两个小时之后, 则服务器会返回 2022/4/26 08:00:00 这个时间对应的值。由于浏览器运行在客户环境下,对于客户而言, 这个缓存已经过期了,虽然缓存确实有效, 但是对于浏览器而言这个缓存确确实实是 “过期了”, 这会导致强制缓存**永远不会生效**!

    那么为了解决这个问题, ***http 1.1 ***协议中添加了 ***Cache-Control*** 中的 ***max-age***, 他是一个相对值, 即客户端获取到这个文件多少秒后失效, 其判别权力全权交由浏览器, 这会相对更准确些。

    **2. 协商缓存**

    协商缓存主要由 ***ETag*** 和 ***Last-Modified*** 两个字段来实现
  • ETag 是一个用于映射 web 资源的映射 token,这个 token 应该满足唯一对应到一 个web服务器上的静态资源(具体实现通常是提取文件相关信息进行hash和base64编码等操作)
  • Last-Modified 则通常是文件最后更新的日期时间戳

(通过上述两个字段就可以判断当前文件是否是最新的数据)

与上述两个字段配对的分别是 ***If-None-Match ***和 ***If-Modified-Since*** 这两个字段, 具体流程如下图所示(03. 协商缓存示意图)

   (03. 协商缓存示意图) 

    浏览器首次向服务器请求数据 A, 服务器正常返回数据,同时在响应头中放入 ***ETag*** 和 ***Last-Modified*** 两个新字段。

    当浏览器第二次向服务器请求数据 A 时, 浏览器会自动地在请求头附上 ***If-None-Match*** 和 ***If-Modified-Since*** 两个字段(分别对应的是 ***ETag*** 和 ***Last-Modified*** 的值,两两相等), 然后由服务器端进行校验, 校验通过的话(表明数据有效), 服务器会直接返回 **状态码 304 **,且不携带响应体的报文段, 这相当于告诉浏览器:**当前缓存有效, 可以直接使用!** 校验失败则会和首次请求一样, 返回状态码为200且携带数据响应体的报文段, 同时这个响应头会带上新的***ETag*** 和***Last-Modified, ***为下一次协商缓存做好铺垫 。

    需要注意的是, 在不用框架的情况下, 协商缓存需要由后端开发人员手动实现,因此 ***ETag*** 和 ***Last-Modified ***两个字段的优先级取决于开发者, 但是 ***Last-Modified*** 这个字段可以记录的时间戳精确度是有一定限制的,如果连续多次数据更新在精确度范围外, 会产生精确度丢失, 因此通常会让***ETag *的优先级高于 *Last-Modified *字段**(类似于***Cache-control***中***max-age***一样, 属于是后续改进协议的一个新字段, 因此优先级一般会高点)

  **  3. 强制缓存 + 协商缓存**

    了解了 **强制缓存 **和 **协商缓存**, 我们不妨看看两者并存的情况,如图(04. 强制缓存和协商缓存联合)所示

(04. 强制缓存和协商缓存联合)

    默认情况下, 浏览器会优先考量**强制缓存**的情况, 当强制缓存生效的情况下, 请求并不会到达服务器, 因此也就不会触发协商缓存。 当强制缓存失效的时候, 浏览器便会将请求传递到服务器, 于是服务器又会开始校验 ***If-Modified-Since*** 和 ***If-None-math*** 两个字段, 重复上述协商缓存的一个执行流程

    乍一看,两者并存的情况, 有点像是两个协议的简单叠加,此时的**协商缓存更像是强制缓存的兜底策略**, 很可能协商缓存很长一段时间都不会生效(强制缓存过期时间设置过长的情况下), 因为强制缓存的优先级是要高于协商缓存的。 当然这并不是我们想看到的, 比方说当后端数据确实变更了, 而此时的浏览器由于使用了**强制缓存**,则会出现数据不一致的情况, 因此在这里引入了请求头中的两个字段 ***no-cache***, 当使用了 ***no-cache*** 字段的时候, 浏览器将不再使用**强制缓存**, 而是直接去请求服务器, 这个时候就会用到**协商缓存**了(顺带一提的是, 还有一个 ***no-store*** 字段, 用了这个字段浏览器则**不会在使用缓存的数据也不缓存数据**,即强制缓存和协商缓存都失效了)

** 4. 缓存机制之间的一些区别**

  1. 强制缓存在缓存有效的情况下不会去请求服务器, 其数据来源则是浏览缓存的本地磁盘。而协商缓存会向服务器请求,但是在协商缓存成功的情况下, 服务器只会返回一个不带响应体的报文,结合开头的背景来说 强制缓存选择“减少过桥次数”的策略, 而协商缓存则是采用 ‘减少过桥人数’的策略
  2. 强制缓存在浏览器强制刷新的情况下不会生效, 而协商缓存则不受影响。(调试代码测试时候,要注意)
  3. 强制缓存返回的报文状态码为 200, 协商缓存返回的报文状态码为 304 (前端使用fetch请求的情况, 协商缓存的 状态码304 会转成 200)
  4. 强制缓存发生在浏览器端, 协商缓存发生在服务器端

三、使用小结

     **强制缓存**和**协商缓存**需要具体条件下来用, 下边是笔者总结的几个小点
  1. **强制缓存**存在一个瓶颈, 当浏览器用户强刷新时,浏览器会直接跳过强制缓存, 这点不注意很容易会被忽视掉。
    
  2. **强制缓存**不适合 SPA 应用的入口文件, 因为重新部署后, 用户如果没有强制刷新, 则无法在第一时间内看到新的网页内容。
    
  3. 作为一个前端开发者可以通过设置请求头中的 no-cache 和 ***no-store 字段选择使用协商缓存*或者不使用缓存!!!

本文转载自: https://blog.csdn.net/m0_63657524/article/details/125966386
版权归原作者 remiliko 所有, 如有侵权,请联系我们删除。

“简析强制缓存和协商缓存”的评论:

还没有评论