Http 浏览器的缓存策略

浏览器第一次向一个 web 服务器发起 http 请求后,服务器会返回请求的资源,并且在响应头中添加一些有关缓存的字段如:Cache-Control、Expires、Last-Modified、ETag、Date 等等。之后浏览器再向该服务器请求该资源就可以视情况使用 强制缓存 和 协商缓存。

  • 强制缓存:浏览器直接从本地缓存中获取数据,不与服务器进行交互。
  • 协商缓存:浏览器发送请求到服务器,服务器判定是否可使用本地缓存。
  • 联系与区别:两种缓存方式最终使用的都是本地缓存;前者无需与服务器交互,后者需要。

下面假定浏览器已经访问了服务器,服务器返回了缓存相关的头部字段且浏览器已对相关资源做好缓存。通过下图来分析强缓存和协商缓存:

浏览器缓存策略

强制缓存

如图红线所示的过程代表强制缓存。用户发起了一个http请求后,浏览器发现本地已有所请求资源的缓存,便开始检查缓存是否过期。有两个 http 头部字段控制缓存的有效期:Expires 和 Cache-Control,浏览器是根据以下两步来判定缓存是否过期的:

  1. 查看缓存是否有 Cache-Control 的 max-age 指令,若有,则使用响应报文生成时间 Date + max-age 获得过期时间,再与当前时间进行对比,判断是否过期;
  2. 如果没有 Cache-Control 的 max-age 指令,则比较 Expires 中的过期时间与当前时间。Expires 是一个绝对时间。
  3. 如果同时定义了两个指令,会优先处理 max-age。

另外,下面列出了几个 Cache-Control 的常用指令:

  • no-cache:含义是不使用强制缓存,需要使用协商缓存。
  • no-store:禁用缓存,既不使用强制缓存,也不使用协商缓存;
  • public:表明其他用户也可使用缓存,适用于公共缓存服务器的情况。
  • private:表明只有特定用户才能使用缓存,适用于公共缓存服务器的情况。

经过上述两步判断之后,若缓存未过期,返回状态码 200,则直接从本地读取缓存,这就完成了整个强制缓存过程;如果缓存过期,则进入协商缓存或者服务器返回新资源。

协商缓存

当浏览器发现缓存过期后,缓存并不一定不能使用了,因为服务器端的资源可能仍然没有改变,所以需要与服务器协商,让服务器判断本地缓存是否还能使用。此时服务器会判断缓存中是否有 ETag 或 Last-Modified 字段,如果没有,则发起一个 http 请求,服务器根据请求返回资源;如果有这两个字段,则在请求头中添加 If-None-Match 字段(有 ETag 字段的话添加)、If-Modified-Since 字段(有 Last-Modified 字段的话添加)。注意:如果同时发送 If-None-Match、If-Modified-Since 字段,服务器只比较 If-None-Match 和 ETag 的内容是否一致;如果内容一致,服务器认为缓存可用,则返回状态码 304,浏览器直接读取本地缓存,这就完成了协商缓存的过程,也就是图中的蓝线;如果内容不一致,则视情况返回其他状态码,并返回所请求资源。下面详细解释下这个过程:

ETag 和 If-None-Match

两者的值都只服务器为每份资源分配的唯一标识字符串。

  • 浏览器请求资源,服务器会在响应报文头中加入 ETag 字段。资源更新时,服务器端的 ETag 值也会随之更新;
  • 浏览器再次请求资源时,会在请求报文头中添加 If-None-Match 字段,它的值就是上次响应报文中的 ETag 的值;
  • 服务器会比对 ETag 和 If-None-Match 的值是否一致,如果不一致,服务器则接受请求(状态码200),返回更新后的资源;如果一直,表明资源未更新,则返回状态码为 304 的响应,可继续使用本地缓存,要注意的是,此时响应头会加上 ETag 字段,即使它没有变化。

Last-Modified 和 If-Modified-Since

二者的值都是GMT格式的时间字符串。

  • 浏览器第一次向服务器请求资源后,服务器会在响应头中加上 Last-Modified 字段,表明该资源最后一次的修改时间;
  • 浏览器再次请求该资源时,会在请求报文头中添加 If-Modified-Since 字段,它的值就是上次服务器响应报文中的 Last-Modified 的值;
  • 服务器会比对 Last-Modified 与 If-Modified-Since 的值是否一致,如果不一致,服务器则接受请求(状态码200),返回更新后的资源;如果一致,表明资源未更新,则返回状态码为 304 的响应,可继续使用本地缓存,与 ETag 不同的是:此时响应头中不会再添加 Last-Modified 字段。

ETag 较之 Last-Modified 的优势

你可能会觉得使用 Last-Modified 已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要 ETag 呢?HTTP1.1 中 ETag 的出现主要是为了解决几个 Last-Modified 比较难解决的问题:

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

这时,利用 ETag 能够更加准确的控制缓存,因为 ETag 是服务器自动生成的资源在服务器的唯一标识符,资源每次变动,都会生成行的 ETag 值。Last-Modified 与 ETag 是可以一起使用的,但服务器会优先验证 ETag。

猜你喜欢

转载自blog.csdn.net/josavion/article/details/115252877
今日推荐