【浏览器】缓存流程

在一个C/S结构中,最基本的缓存分为两种:

  • 客户端缓存
  • 服务器缓存

以下重点讲客户端缓存

所谓客户端缓存,顾名思义,是将某一次的响应结果保存在客户端(比如浏览器)中,而后续的请求仅需要从缓存中读取即可,极大的降低了服务器的处理乐力。

来自服务器的缓存指令

当客户端发出一个get请求到服务器,服务器希望客户端将相应资源缓存起来,服务器在响应头中加入了以下内容:

Cache-Control:max-age=3600
ETag:W/"121-171ca289ebf"
Date:Thu,30 Apr 2020 12:39:56 GMT
Last-Modified:Thu, 30Apr2o20 08:16:31GMT

这个响应头表达了下面的信息:

  • Cache-Control:max-age=3600, 缓存时间是3600秒(1小时)
  • ETag:W/“121-171ca289ebf”, 这个资源的编号是W/“121-171ca289ebf”
  • Date:Thu,30Apr202012:39:56GMT, 我给你响应这个资源的服务器时间是格林威治时间2020-04-3012:39:56
  • Last-Modified:Thu,30Apr2020g8:16:31GMT, 这个资源的上一次修改时间是格林威治时间2020-04-3088:16:31

如果客户端是其他应用程序,可能并不会理会服务器的愿望,也就是说,可能根本不会缓存任何东西。

如果刚好是一个浏览器,那么就会执行缓存:

  • 浏览器把这次请求得到的响应体缓存到本地文件中
  • 浏览器标记这次请求的请求方法和请求路径
  • 浏览器标记这次缓存的时间是3600秒
  • 浏览器记录服务器的响应时间是格林威治时间2020-04-3012:39:56
  • 浏览器记录服务器给予的资源编号W/“121-171ca289ebf”
  • 浏览器记录资源的上一次修改时间是格林威治时间2020-04-3088:16:31

来自客户端的缓存协议

当客户端准备再次请求GET /index.js时,它突然想起了一件事:我需要的东西在不在缓存里呢?
此时,客户端会到缓存中去寻找是否有缓存的资源
寻找的过程如下:

  1. 缓存中是否有匹配的请求方法和路径?
  2. 如果有,该缓存资源是否有效呢?

以上两个验证会导致浏览器产生不同的行为

image.png

缓存无效(协商缓存)

当浏览器发现缓存已经过期,它并不会简单的把缓存删除,而是抱着一丝希望,想问问服务器,我这个缓存还能继续使用吗?

于是,浏览器向服务器发出了一个带缓存的请求,又称之为协商缓存。

所谓带缓存的请求,无非就是加入了以下的请求头:

If-Modified-Since: Thu,30 Apr 2020 08:16:31 GMT
If-None-Match: W/"121-171ca289ebf"

它们表达了下面的信息:

  • If-Modified-Since:Thu,30Apr202088:16:31GMT 这个资源
    的上一次修改时间是格林威治时间2020-04-3088:16:31,请问这个资源在这个时间之后有
    发生变动吗?
  • If-None-Match:W/“121-171ca289ebf” 这个资源的编号是W/"121
    171ca289ebf, 请问这个资源的编号发生变动了吗?

其实,这两个问题可以合并为一个问题:就是问服务器资源到底变化了没有!

之所以要发两个信息,是为了兼容不同的服务器,因为有些服务器只认If-Modified-Since,有
些服务器只认If-None-Match(优先级更高),有些服务器两个都认。

服务器可能会产生两个情况:

  • 缓存已经失效
  • 缓存仍然有效

如果是第一种情况一一缓存已经失效,那么非常简单,服务器再次给予一个正常的响应(响应码 200 带响应体),同时可以附带上新的缓存指令,这就回到了上一节一一来自服务器的缓存指令。

这样一来,客户端就会重新缓存新的内容。

但如果服务器觉得缓存仍然有效,它可以通过一种极其简单的方式告诉客户端:

  • 响应码为 304 Not Modified
  • 无响应体
  • 响应头带上新的缓存指令,见上一节一一来自服务器的缓存指令

这样一来,就相当于告诉客户端:「你的缓存资源仍然可用,我给你一个新的缓存时间,你那边更新一下就可以了」

于是,客户端就继续使用缓存了。

这样一来,可以最大程度的减少网络传输,因为如果资源还有效,服务器就不会传输消息体

细节

Cache-Control

Cache-Control 是一个响应头,Cache-Control 是现代缓存控制的主要头部字段,定义了浏览器和中间代理服务器如何缓存资源。

常见指令:

  • public:允许所有缓存(包括 CDN 等中间缓存)缓存资源。
  • private:只能由客户端缓存,不能由共享缓存(如代理服务器)缓存。
  • no-cache: 告知客户端,你可以缓存这个资源,但是不要直接使用它。当你缓存之后,后续的每一次请求都需要附带缓存指令,让服务器告诉你这个资源有没有过期。如果资源没有变化,服务器可以返回 304 Not Modified 响应,浏览器使用缓存副本。
  • no-store: 告知客户端,不要对这个资源做任何的缓存,之后的每一次请求都按照正常的普通请求进行。
  • max-age: 需要缓存的时间(s),资源在指定的秒数内可以直接从缓存读取。

Expires

http1.1 使用 Expires 来指定过期时间(旧的方式)。

ETag(Entity Tag)

ETag 是资源的唯一标识符,通常由服务器生成。浏览器在后续请求中可以通过 If-None-Match 头部发送此 ETag,服务器会根据这个值判断资源是否发生变化。

如果资源未修改,服务器会返回 304 Not Modified,并且浏览器继续使用缓存中的副本。

Last-Modified

表示资源最后一次修改的时间戳。浏览器可以在后续请求中发送 If-Modified-Since 头部,服务器会基于这个时间判断资源是否发生变化。
如果未修改,服务器返回 304 Not Modified 响应。

记录缓存时的有效期(判断浏览器是否使用缓存)

浏览器会按照服务器响应头的要求,自动记录缓存到本地文件,并设置各种相关信息。

在这些信息中,有效期尤为关键,它决定了这个缓存可以使用多久。

浏览器会根据服务器不同的响应情况,设置不同的有效期。

具体的有效期设置,按照下面的流程进行:

  1. 查看 Cache-Control 和 Expires 头部:

    浏览器会首先检查资源的缓存是否已过期。如果没有过期,并且 Cache-Control 允许使用缓存,浏览器直接从缓存中获取资源。

  2. 验证缓存有效性(ETag 和 Last-Modified):

    如果缓存已经过期,浏览器会通过发送请求带上 ETag 或 If-Modified-Since 进行验证,服务器判断资源是否更新。

    如果资源没有更新,服务器返回 304 Not Modified,浏览器继续使用缓存资源。

  3. 不使用缓存的情况:

    如果 Cache-Control 设置了 no-cache 或 no-store,浏览器不会缓存资源,必须重新从服务器获取。

image.png

Pragma

这是 http1.0 版本的消息头。

当该消息头出现在请求中时,是向服务器表达:不要考虑任何缓存,给我一个正常的结果。

在http1.1版本中,可以在请求头中加入Cache-Control:no-cache实现同样的含义。

在Chrome浏览器中调试时,如果勾选了Disable cache,则发送的请求中会附带该信息。

Vary

有的时候,是否有缓存,不仅仅是判断请求方法和请求路径是否匹配,可能还要判断头部信息是否匹配。

此时,就可以使用Vary字段来指定要区分的消息头。比如,当使用GET /personal.html请求服务器时,请求头中cookie的值不一样,得到的页面也不一样。

如果还按照之前的做法,仅仅匹配请求方法和请求路径,如果 cookie 变动,你可能得到的仍然是之前的页面。

使用版本号或hash

当我们打包的结果中很多文件名,类似于这样:app.68297cd8.css

文件的中间部分使用了hash值。

这样做的好处是,可以让客户端大胆的、长时间的缓存该文件,减轻服务器的压力。

当文件改动后,它的文件hash值也会随之而变,比如变成了app.446fccb8.css。

这样一来,客户端要请求新的文件时,就会发现路径从/app.68297cd8.css变成了/app.446fccb8.css, 由于之前的缓存路径无法匹配到,因此就会发送新的请求来获取新资源了。以上是现代流行的做法。

而在古老的年代,还没有构建工具出现时,人们使用的办法是在资源路径后面加入版本号来获取新版本的文件。文件改变,需要手动改变路径。

总结

服务端:

image.png

客户端:

image.png

HTTP 缓存可以通过以下⼏种⽅式进⾏实现:

  1. 浏览器缓存:浏览器可以将最近请求过的资源保存到本地,下次请求时可以直接从本地读取,从⽽提⾼访问速度。
  2. 代理缓存:代理服务器可以缓存响应,减少对原始服务器的请求次数,从⽽加快响应速度。
  3. ⽹关缓存:⽹关可以缓存来⾃多个原始服务器的响应,然后将响应发送到客户端,从⽽提⾼性能。

HTTP 缓存可以分为协商缓存和强制缓存两种类型:

强制缓存是指浏览器在请求资源时,不会发送任何请求头,直接从本地缓存中读取资源,从⽽提⾼响应速度。常⻅的实现⽅式包括:

  1. Expires 头部:指定资源过期的时间,如果在过期时间之前再次请求该资源,浏览器将直接从缓存中读取资源。
  2. Cache-Control 头部:可以指定资源的缓存策略,包括 public、private、no-cache 等,控制浏览器的缓存⾏为。

协商缓存是指浏览器在请求资源时,会发送⼀些请求头到服务器,询问服务器资源是否已经发⽣改变。如果资源未发⽣改变,服务器将返回 304 状态码,告诉浏览器可以从缓存中读取资源,从⽽减少了⽹络带宽的使⽤。常⻅的实现⽅式包括:

  1. Last-Modified / If-Modified-Since 头部:浏览器在请求资源时,会将资源最后修改时间(Last-Modified)发送到服务器,服务器检查资源是否发⽣变化,如果没有发⽣变化,返回 304 状态码,否则返回新的资源。
  2. ETag / If-None-Match 头部:服务器可以给每个资源分配⼀个唯⼀的标识符(ETag),浏览器在请求资源时,将该标识符发送到服务器,服务器检查资源是否发⽣变化,如果没有发⽣变化,返回 304 状态码,否则返回新的资源。

需要注意的是,协商缓存虽然可以减少⽹络带宽的使⽤,但是需要服务器进⾏资源⽐较,因此会增加服务器的负载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秀秀_heo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值