图说 HTTP 缓存

无论是软件应用还是硬件应用,缓存都扮演着重要的角色,其对提升性能的重要性无可置疑。

本文主要介绍 HTTP 缓存,涉及其原理和应用。HTTP 缓存主要通过 HTTP 首部来控制。

缓存示例

先看一个简单的缓存示例:

HTTP Cache

HTTP 缓存涉及到请求-响应链上的多个角色,包括客户端(本文指浏览器)、代理和服务器。
其中,浏览器自身也实现了缓存功能。浏览器在请求资源时,总是先从本地缓存中查找,如果找到未过期资源,则直接使用,否则向服务器发起请求。
代理也是服务器的一种,但一般情况下不会把它单独抽出来分析,只有在跟它有关的地方会把它区分于源服务器。所以,下文的示例图中将不会把它列进去。

HTTP 缓存的理解基本上可以总结为三个问题:

  1. 缓存数据可以存储在哪些设备上WHERE)
  2. 缓存数据如何判断过期HOW)
  3. 过期缓存内容是否真的需要重新下载WHETHER)

问题 1 说明存储缓存数据的设备是多样的,可以存储于各级代理服务器,也可以存储于浏览器本地。

问题 2 说明使用什么办法来判断缓存数据是否已经过期,当然是比较时间啦,那么如何比较呢/p>

问题 3 说明缓存虽然过期了,但是其内容仍然可能与服务端一致,这时就没必要重新下载相同数据,只需要向服务端询问下是否可以继续使用缓存即可。

带着上面三个问题去理解 HTTP 缓存头部设置会更有助于理解和记忆。

有人根据是否需要进行问题 3 中的重新验证把缓存策略的设置分为强缓存和协商缓存,强缓存无须再次验证的缓存策略,协商缓存是需要再次验证的缓存策略。
两者的区别在于,协商缓存多发起了一次 HTTP 请求。

缓存首部

HTTP 缓存主要通过 HTTP 首部来实现缓存控制。这些与缓存相关的 HTTP 首部这里统称为缓存首部,具体首部如下表所示。

首部字段 首次定义 首部类型
Pragma HTTP/1.0 通用首部
Age HTTP/1.1 响应首部
Expires HTTP/1.0 实体首部
Cache-Control HTTP/1.1 通用首部
Etag HTTP/1.1 响应首部
If-Match HTTP/1.1 请求首部
If-None-Match HTTP/1.1 请求首部
If-Modified-Since HTTP/1.0 请求首部
Last-Modified HTTP/1.0 实体首部

其中,“首次定义”是指首次出现在哪个 HTTP 版本。之所以列出这项内容,是因为实际应用需要考虑兼容旧版 HTTP

现代的 HTTP 缓存策略主要使用 实现,它是目前最新的缓存首部,用于取代较老的缓存首部如 、 等。所以应用中应该倾向于使用 。但是为了支持只实现了 HTTP/1.0 的客户端设备,服务端通常还是都会同时设置 、 和 等,此时 会有更高的优先级。提醒一下,现代浏览器都已支持 。

Cache-Control

通用首部,这意味着它既可以出现在请求中,也可以出现在响应中。

的值可由多个字段组合而成,以逗号分隔,如 。下面对常用的可取字段进行说明。

: 表示当前响应数据所有用户共享的,可以被任何设备缓存,包括客户端、代理服务器等。

: 表示当前响应数据是单个用户所独占的,只能被客户端缓存,不能被代理服务器缓存。

: 指定缓存的有效时间,单位为秒。其值是任意整数,0 和负数表示缓存过期,正数值加上当前响应头中的 首部值即为过期时间。

: 只用于请求,表示客户端仍然愿意接受过期缓存,只要过期时间没超过指定时间,如果未指定时间,则表示任何过期的时间。

: 只用于请求,表示客户端愿意接受还剩余多少秒过期的缓存。

: 功能与 一致,但它仅作用于共享缓存,对私有缓存无效。

: 并非字面意思,它并非禁止缓存,而是强制在使用已缓存数据之前,需要去服务端验证一下是否可以使用缓存数据。

: 真正的禁止缓存,任何设备都不允许缓存,每次请求都需要向服务端重新获取数据。

: 表示响应的实体数据不应被转换。 、 和 首部也不能被修改。实际应用中,有些代理服务器会对图片资源进行格式转换以节省空间或者带宽。

作为通用首部,其部分指令值可以出现在请求首部,也可以出现在响应首部,两者可能略有区别:

指令值 请求 响应
可共享数据,可被任何设备缓存
用户私有数据,只能被客户端缓存
使用前需验证 使用前需验证
禁止使用缓存数据 禁止缓存
要求资源的 age 小于这个时间 最大过期时间
要求资源至少还剩余多少过期时间
超过过期时间多少秒内仍愿意接受
不要转换格式 不要转换格式

这些指令用在请求首部的情况比较少见,最可能接触的地方是 中的 标签页。
其中,有个 选项,选中后 会自动给所有请求头部加上 首部,以告诉浏览器和代理使用本地缓存之前必须先验证。

If-Modified-Since

  1. 客户端首次请求 时,服务器响应带上 首部,告诉客户端当前资源的最后修改时间。客户端根据 ,把 和响应首部缓存起来。
  2. 客户端再次发起请求 时,把之前保存的 时间放入 首部发给服务器。服务器发现资源的 时间没有发生改变,于是直接响应 304 。客户端收到 304 后,直接使用缓存的 ,同时更新缓存有效期。
  3. 客户端再次发起请求 时,把之前保存的 时间放入 首部发给服务器。服务器发现资源的 时间已经发生改变,于是响应 200 ,将修改后的 和新的 发送给客户端。客户端收到 200 后,重新下载新的 ,并把新的 和响应首部缓存起来,替换原先的旧缓存。

ETag/If-Matched/If-None-Match

叫实体标签(Entity Tag),用于表示实体资源是否发生变化,其生成原理类似 MD5 ,也是一种用于验证的首部。当响应的首部信息或者消息实体发生变化时,实体标签也会改变。

使用过程如下:

ETag

  1. 客户端首次请求 时,服务器响应带上 首部,告诉客户端当前资源的实体标签。客户端根据 ,把 和响应首部缓存起来。
  2. 客户端再次发起请求 时,把之前保存的 值放入 首部发给服务器。服务器发现自己的资源 值并没有发生改变,于是直接响应 304 。客户端收到 304 后,直接使用缓存的 ,同时更新缓存有效期。
  3. 客户端再次发起请求 时,把之前保存的 值放入 首部发给服务器。服务器发现自己的资源 值已经发生改变,于是响应 200 ,将修改后的 和新的 发送给客户端。客户端收到 200 后,重新下载新的 ,并把新的 和响应首部缓存起来,替换原先的旧缓存。

当客户端本地存储有多个版本的资源时,会把所有的实体标签都上传,形如 ,服务端会使用 首部返回匹配中的实体标签值。

实体标签分为强标签(Strong ETag)和弱标签(Weak ETag),弱标签以 开头,如 。强标签使用强比较,弱标签使用弱比较。强比较意味着两个比较对象的每一个字节都相同,弱比较意味着两者语义相同(Semantic Equivalence)。举个栗子,假如响应首部包含一个渲染时间 ,A 响应的渲染时间为 365,B 响应的渲染时间为 345,两个响应的实体内容一致。这种情况下,我们可以说 A 和 B 弱比较相等,强比较不相等。

一般来说,静态内容使用强标签,动态生成的内容使用弱标签

由此可以看出,实体首部可以解决一些 无法解决的问题:

  1. 某些服务器不能得到文件的精确的最后修改时间
  2. 修改时间变了并不意味着内容的改变,比如改完保存后又改回去
  3. 修改时间只能精确到秒,一秒内的修改无法判断

和 的另一种用法:避免“空中碰撞”,以防编辑冲突。当客户端使用 PUT 或者 POST 更新服务端资源时,需要使用 来携带实体标签给服务端,以确保客户端要修改的资源没有被别人修改过,避免覆盖别人的修改。不过这种用法比较少,可以不用深究。

Expires

指明资源的过期时间,如 。非法的日期格式(如 0)将会被当做过去的时间,表示该资源已经过期。

如果 和 的 或者 同时出现, 将被忽略

Age

表示资源在代理服务器上已经缓存了多久时间,单位为秒。如果是 ,表明该资源刚刚从服务器获取。它的计算方式一般使用代理服务器当前的时间减去缓存资源的 时间。

Pragma

是 HTTP/1.0 中引入的首部,现在使用时一般用于向后兼容 HTTP/1.0,不鼓励使用。

的作用与 一致,表示需要跟服务器进行验证后才能使用缓存资源。

启发式缓存策略

并不是每个服务器都会返回明确的缓存策略,这种情况下客户端会采取启发式缓存策略。注意,只有在服务端没有返回明确的缓存策略时才会激活启发式缓存策略。

启发式缓存策略会根据其他的首部信息来计算一个过期时间,其他的首部通常是 和 。此时,缓存有效期一般取两者差值的 10% 。

使用启发式缓存策略时,如果超过当前时间 24 小时且从未警告过,浏览器或者代理服务器应该在响应中产生一个警告首部字段 。

参考资料

  • HTTP/1.0, RFC1945
  • HTTP/1.1, RFC2616
  • HTTP/1.1 Caching, RFC7323
  • HTTP 缓存, Google Developers
  • The-difference-between-strong-and-weak-ETags

文章知识点与官方知识档案匹配,可进一步学习相关知识网络技能树首页概览22055 人正在系统学习中 相关资源:实例讲解分布式缓存软件Memcached的Java客户端使用-其它代码类…

来源:weixin_33770878

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2018年2月5日
下一篇 2018年2月5日

相关推荐