欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

我与缓存的相爱相杀

程序员文章站 2022-07-14 16:50:16
...

缓存是我们这些切图仔绕不开的话题,但是你不理解它就会出现需要时,拿不到,不需要时,总是出来作怪,今天这篇文章将带梳理下于缓存有关的知识。

那么什么是缓存呢?

缓存是浏览器的一种机制,叫浏览器缓存机制,该机制可以把一个请求过的 Web 资源(如 html 页面,图片,js,api数据等)拷贝一份到副本存储在浏览器中,并根据请求配置选择是否使用它们。

为什么使用缓存?

  • 减少网络带宽消耗
  • 降低服务器压力
  • 减少网络延迟,加快页面打开速度

浏览器端缓存规则:对于浏览器的缓存来讲,这些规则是在 HTTP 协议头和 HTML 页面的 Meta 标签中定义的。

浏览器的缓存控制

① 使用 HTML Meta标签

我们可以在 HTML 页面的节点中加入标签

  <!- Pragma 是 http1.0 版本中给客户端设定缓存方式之一 ->
  <meta http-equiv="Pragma" content="no-cache">
复制代码

上述代码含义是:浏览器当前页面不被缓存,每次访问都向服务器请求,但是通过这种方式去禁用缓存的形式用处很有限

  • 仅有 IE 才能识别这段 meta 标签含义,其它主流浏览器仅识别 "Cache-Control: no-store" 的mete标签
  • 在 IE 识别到该meta标签含义,并不一定会在请求字段加上 Pragma,但的确会让当前页面每次都发新请求(仅限页面,页面上的资源不受影响)

② 使用缓存有关的 HTTP 消息报头,这个后面会详细说

与缓存有关的消息报头有expires,cache-control,pragma,Last-Modified,If-modified-since,Etag,If-none-match 等。

浏览器的缓存机制可分为四个方面

  • Memory Cache
  • Service Worker Cache
  • HTTP Cache
  • Push Cache

这里我们只研究 HTTP Cache

为什么 HTTP 缓存可以提升效率呢?

我们知道,请求是个复杂的过程,需要经过

DNS解析 -> 因特网的五层协议 -> 服务器 -> 服务器又要进行安全校验 -> 根据请求执行相应的代码 -> 封装成 HTTP 请求 -> 四次挥手

最后我们的浏览器才拿到数据,而缓存就是把这一系列繁琐的过程都省略掉,当下一个请求来到时,如果是相同的 URL,直接读取相应的数据。

如果你还不理解,这里举个例子,商品的总部在北京,咦,它发现广东这边的市场对这个产品的需求量很大,那么它就在广州建一个仓库,那么广东这边购买该产品时,直接从广州发货,大大节省了时间,提升了效率.

好了,经过上面的解释,我们理解了,缓存是一种浏览器的机制,它会拷贝一份副本数据存储在本地,当遇到相同的请求,直接从本地读取数据,通过重复利用之前获取资源的方式来减少IO消耗,从而提高了访问速度,所以说,缓存是性能优化的一种手段。

既然缓存可以提高效率,那要怎么用呢? 不可能毫无章法吧,没错,确实有一套缓存规则,当输入 url,会进行一次数据请求,返回 http 响应头,就会携带相应的缓存规则,因此,要缓存规则可以由服务器决定。

HTTP缓存有许多规则,根据是否需要重新向服务器发起请求来分类,我将其分为两大类(强缓存和协商缓存)

顾名思义,强缓存意味着强制使用缓存,协商缓存意味着每用一次缓存都要协商一次。 强缓存和协商缓存都允许使用情况下,优先强缓存。

强缓存

控制字段:

  • Expires: HTTP1.0
  • Cache-Control: HTTP1.1 判断过程:请求再次发起 -> 浏览器根据 expires 和 cache-control 判断目标资源是否命中"强缓存" -> 若命中,直接从缓存获取资源,不再与服务器发生通讯。

Expires

响应头中的expires

expires: Sat,30 Mar 2019 10:31:59 GMT

流程

  • 首次请求
  • 服务器告知启用强缓存,并在响应头中带上 expires,告知缓存到期时间,该值是个时间戳。
  • 随后的每次请求,浏览器会先对比本地时间和 expires 的时间戳。
  • 如果本地时间小于 expires 设定的过期时间,那么就直接去缓存中取这个资源。

弊端: 依赖本地时间,如果用户修改了本地时间,那么 expires 就无法达到我们的预期。

基于对本地时间依赖这个弊端,HTTP1.1提出了 Cache-Control 来完全替代 expires 的任务,当两者并存情况下,优先 Cache-Control,在当下的前端实践里,继续使用 expires 的唯一目的就是向下兼容

Cache-Control

响应头中的 Cache-Control

cache-control: max-age=655350000

cache-control 中常用的值

max-age:指定的是从文档被访问后的存活时间,这个时间是个相对值,相对的是文档第一次被请求时服务器记录的。

Request_time(请求时间),也就是,相对的是文档的请求时间(Atime),单位:s。

s-maxage: 表示向代理服务器请求缓存内容,只在代理服务器中生效,优先级高于 max-age,客户端中只考虑 max-age。

pubilc 与 private: 针对资源是否能够被代理服务器缓存而存在的一组对立概念,如果设置了 public,那么它既可以被浏览器缓存,也可以被代理服务器缓存,如果我们设置了 private,则只能被浏览器缓存,默认值是 private。 no-cache: 每一次发起请求都不会再去询问浏览器缓存情况,而是直接向服务器去确认资源是否过期(等于直接使用协商缓存)。 no-store: 不使用任何缓存策略,直接向服务端发送请求,并下载完整的响应。

已存在缓存数据时,仅基于强制缓存,请求数据流程如下:

强缓存生效的网络请求图

状态码为灰色的请求则代表使用了强制缓存,请求对应的Size值则代表该缓存存放的位置,分别为from memory cache 和 from disk cache。

协商缓存

控制字段: (HTTP1.0)

  • Last-Modified:是一个时间戳,由服务器生成,如果规则中启用了协商缓存,它会在首次请求时,随着 Response Headers 返回,存在响应头中。
  • If-Modified-Since:也是一个时间戳,依托着 Last-Modified,是它的复制品,由浏览器生成,在启用协商缓存情况下,每次的请求头中都会带上这个字段,它的值等于上一次响应头返回的 last-modified 值。 (HTTP1.1)
  • Etag:服务器为每个资源生成的唯一的标识字符串,这个标识字符串时基于文件内容编码的,只要文件内容不同,它们对应的 Etag 就是不同的,由服务器产生,存在响应头中。
  • If-None-Match: Etag 的复制品,用法和 If-Modified-Since 一样,存在请求头中。

如果服务器提示资源未改动(Not Modified),资源会被重定向到浏览器缓存,这种情况下,网络请求对应的状态码是304。

Last-Modified 与 If-Modified-Since

响应头中的 Last-Modified

last-modified: Wed,19 Dec 2018 08:57:33 GMT 请求头中的 If-Modified-Since If-Modified-Since: Mon,14 Jan 2019 03:31:14 GMT

流程

  • 首次请求
  • 服务器告知启用协商缓存规则,并在响应头中带上 Last-Modified,告知缓存到期时间
  • 随后的每次请求,请求头上都会携带 If-Modified-Since,该值等于上一次响应头中的 Last-Modified 的值
  • 服务器收到 If-Modified-Since 后,会将该属性的值与服务器上资源的最后修改时间进行匹配,从而判断资源是否发生了变化
  • 如果发生变化会返回一个完整的响应内容,在响应头中添加新的 Last-Modified 值,否则,只返回header部分,状态码为304,响应头不会再添加 Last-Modified.

弊端: Last-Modified无法正确感知文件的变化,譬如说,文件的编辑时间修改了而内容没有修改,或者修改文件速度太快,几毫秒就改一次文件,If-Modified-Since 只能检测秒级的变化.

为了解决这个问题,Etag 作为 Last-Modified 的升级版,因时而生

Etag 是通过标识字符串来辨别文件内容是否发生修改的,文件内容不一致才会生成新的标识字符串,这就弥补了 Last-Modified 时间戳的不足,通过 Etag 可以精准的感知文件的变化.

Etag 与 If-None-Match

响应头中的 Etag

etag: "2c6bee6a6ab9e39b892970e9368a3dff" 请求头中的 If-None-Match If-None-Match: "2c6bee6a6ab9e39b892970e9368a3dff"

流程

  • 首次请求
  • 服务器启用协商缓存情况下,会在响应头中带上 Etag
  • 随后每次请求,请求头上都会带上 If-None-Match,该值等于上一次响应头中的 Etag 的值
  • 服务器收到 If-None-Match 后,会进行比对,从而判断资源是否发生变化
  • 如果变化返回一个完整响应内容,在响应头上添加新的 Etag 值,否则返回 304,响应头不会在添加 Etag

弊端: Etag的生成需要服务器付出额外的开销,会影响服务端性能

Etag 并不能替代 Last-Modified,只能作为 Last-Modified 的补充和强化存在,当 Etag 和 Last-Modified 同时出现时,以 Etag 为准

已存在缓存数据时,仅基于协商缓存,请求数据的流程如下

协商缓存生效的网络请求图

不能缓存的请求

  • HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
  • 需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
  • 经过HTTPS安全加密的请求
  • POST请求无法被缓存
  • HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存

转载于:https://juejin.im/post/5cb714f26fb9a068973ed5bf