阿夸漫谈

缓存之客户端缓存:原理篇

发表于 2019-02-15

缓存分为客户端缓存、代理缓存、服务端缓存。选择恰当的缓存策略可以大大减少加载时间。本文将重点讲述客户端缓存。

1. 客户端缓存(浏览器缓存)

1.1 强缓存与协商缓存

客户端缓存又称为浏览器缓存,可以分为强缓存与协商缓存两种。

强缓存通过 http 头中的 PragmaCache-ControlExpires 来控制,如果缓存可用,则不向服务器请求。

协商缓存通过 http 头中的 Last-ModifiedETag 控制,浏览器发送请求到服务器,服务器根据相关 http 头进行判断缓存是否可用。

两者的区别如下:

  相关 http 头 缓存可用时状态码 缓存可用时是否请求
强缓存 Pragma、Cache-Control、Expires 200 OK (from memory cache) 不请求,直接从本地获取
协商缓存 Last-Modified、ETag 304 Not Modified 请求,由服务端判断是否使用缓存

1.2 执行优先级

  1. Pragma
  2. Cache-Control
  3. Expires
  4. Last-Modified/If-Modify-Since 和 ETag/If-None-Match

前三个都是强缓存,不请求服务器,所以最先执行。第四个是协商缓存,请求后才执行,Last-ModifiedETag 并没有优先级。如果都存在,则需要两者都符合才返回 304,即使用缓存。

2. 客户端缓存相关 http 头

客户端缓存主要通过 http 头进行设置,下面逐一了解相关 http 头。

2.1 Pragma/Cache-Control/Expires

PragmaCache-Control 两者类似, Pragma 是在 HTTP/1.0 中规定的通用首部,Cache-Control 是在 HTTP/1.1 中规定的通用首部。

PragmaCache-Control 在请求头和响应头中都能设置,但是意思完全不同的。下面以 Cache-Control 常见值为例:

Cache-Control 字段值 请求头中使用 响应头中使用
no-cache 告知服务端不使用缓存,直接发起请求 告知客户端不进行缓存
max-age = s 告知服务端愿意接受缓存时间超过 s 秒的资源,s 为 0 则接受任意缓存时间资源 告知客户端在 s 时间内可以直接使用缓存,不用请求,s 为 0 则不缓存

Expires 是在 HTTP/1.0 中规定的,通过在响应头中设置日期/时间,例如 expires: Thu, 14 Feb 2019 11:40:56 GMT,日期内代表缓存可用,不用再请求服务端,需要注意的是这个时间指格林尼治时间,北京时间还要加 8 个小时

三者的优先级为 Pragma > Cache-Control > Expires,但是 Pragma 在响应中没有确切规范,不能可靠替代 HTTP/1.1 中通用首部 Cache-Control,所以建议使用 Cache-Control,仅在需要兼容 HTTP/1.0 的时候使用 Pragma

2.2 Last-Modified/If-Modify-Since

Last-Modified 是一个响应首部,记录服务器上资源的修改时间;If-Modified-Since 是对应的请求首部,值为上次请求返回响应头中 Last-Modified 的值。
last-modified

  1. 客户端发起请求,请求头中 If-Modified-Since 的值为上次请求返回响应头中 Last-Modified 的值。
  2. 服务器在收到请求时,对比服务器上资源最后的修改时间和请求中 If-Modified-Since 的值是否一致。
  3. 两者值是若一致,代表缓存可用,返回状态码 304;若不一致,返回状态码 200,更新响应头 Last-Modified 的值,同时携带最新的资源。
  4. 客户端收到响应,保存 Last-Modified 的值,作为下次请求 If-Modified-Since 的值。

Last-Modified 的缺点:

  1. 最小时间为秒,如果短时间的发生多次修改无法区分。
  2. 内容不改变,修改时间改变缓存也不可用。

2.3 ETag/If-None-Match

ETag 是一个响应首部,根据文件内容生成的 hash 值,所以避免了 Last-Modified 因为时间而产生的缺点。If-None-Match 是对应的请求首部,值为上次请求返回响应头中 ETag 的值。
etag
ETagLast-Modified 流程相似:

  1. 客户端发起请求,请求头中 If-None-Match 的值为上次请求返回响应头中 ETag 的值。
  2. 服务器在收到请求时,对比服务器上根据资源生产的 hash 值和请求中 If-None-Match 的值是否一致。
  3. 两者值是若一致,代表缓存可用,返回状态码 304;若不一致,返回状态码 200,更新响应头 ETag 的值,同时携带最新的资源。
  4. 客户端收到响应,保存 ETag 的值,作为下次请求 If-None-Match 的值。

(完)

目录