title: HTTP缓存技术详解
date: 2018-05-21 14:20:06
tags:
- HTTP
- 缓存
categories: 深入http
通过网络获取内容既缓慢,成本又高:大的响应需要在客户端和服务器之间进行多次往返通信,这拖延了浏览器可以使用和处理内容的时间,同时也增加了访问者的数据成本。因此,缓存和重用以前获取的资源的能力成为优化性能很关键的一个方面。
与缓存相关的HTTP头部字段
http1.0时期的缓存方案
头部名称 | 说明 |
---|---|
Pragma | 控制缓存行为,如果设置为no-cache,表示禁用缓存。和http1.1中的Cache-Control头部功能相似 |
Expires | 过期时间,用的是服务器的时间,如果客户端和服务器时间不一致,则会存在缓存时间误差。http1.1可以用Cache-Control来实现相似的功能。 |
如果使用了Pragma: 'no-cache'的话,再设置Expires或者Cache-Control,就没有用了,说明Pragma的权值比后两者高。
如果设置了Expires之后,客户端在需要请求数据的时候,首先会对比当前系统时间和这个Expires时间,如果没有过那个时间,则直接读取本地磁盘中的缓存数据,不发送请求。
http1.1的缓存方案
通用头部字段,即请求和响应都可以包含
头部名称 | 说明 |
---|---|
Cache-Control | 控制缓存行为 |
Pragma | http1.0的字段,作用和Cache-Control大体相同 |
- Cache-Control作为请求头部
指令 | 参数 | 说明 |
---|---|---|
no-cache | 无 | 强制向源服务器再次验证 |
no-store | 无 | 不缓存请求或相应的任何内容 |
max-age=[秒] | 必需 | 相应的最大Age值 |
max-stale=(=[秒]) | 可省略 | 接收已过期的响应 |
min-fresh=[秒] | 必需 | 期望在指定时间内的响应仍有效 |
no-transform | 无 | 代理不可更改媒体类型 |
only-if-cached | 无 | 从缓存获取资源 |
cache-extension | - | 新指令标记(token) |
Cache-Control: no-cache
使用 no-cache 指令的目的是为了防止从缓存中返回过期的资源。 客户端发送的请求中如果包含 no-cache 指令,则表示客户端将不会接 收缓存过的响应。于是,“中间”的缓存服务器必须把客户端请求转发 给源服务器。
Cache-Control: max-age=604800(单位:秒)
当客户端发送的请求中包含 max-age 指令时,如果判定缓存资源的缓 存时间数值比指定时间的数值更小,那么服务端就直接返回304,客户端会使用自己本地缓存的资源。 另外,当指定 max-age 值为 0,那么服务器就会使用ETag和modefied-time验证,来决定返回304还是200。
应用 HTTP/1.1 版本的缓存服务器遇到同时存在 Expires 首部字段的情 况时,会优先处理 max-age 指令,而忽略掉 Expires 首部字段。而 HTTP/1.0 版本的缓存服务器的情况却相反,max-age 指令会被忽略。
- Cache-Control作为响应头部
指令 | 参数 | 说明 |
---|---|---|
public | 无 | 可向任意方提供响应的缓存 |
private | 可省略 | 仅向特定用户返回响应 |
no-cache | 可省略 | 缓存前必需先确认其有效性 |
no-store | 无 | 不缓存请求或相应的任何内容 |
no-transform | 无 | 代理不可更改媒体类型 |
must-revalidate | 无 | 可缓存但必须再向源服务器进行确认 |
proxy-revalidate | 无 | 要求中间缓存服务器对缓存的响应有效性再进行确认 |
max-age=[秒] | 必需 | 响应的最大Age值 |
s-maxage=[秒] | 必需 | 公共缓存服务器响应的最大Age值 |
cache-extension | - | 新指令标记(token) |
Cache-Control: public
当指定使用 public 指令时,则明确表明其他用户也可利用缓存。
Cache-Control: private
当指定 private 指令后,响应只以特定的用户作为对象,这与 public 指令的行为相反。 缓存服务器会对该特定用户提供资源缓存的服务,对于其他用户发送 过来的请求,代理服务器则不会返回缓存。
Cache-Control: no-cache
如果服务器返回的响应中包含 no-cache 指令,那么缓存服务器不能对 资源进行缓存。源服务器以后也将不再对缓存服务器请求中提出的资 源有效性进行确认,且禁止其对响应资源进行缓存操作。
Cache-Control: no-cache=Location
由服务器返回的响应中,若报文首部字段 Cache-Control 中对 no-cache 字段名具体指定参数值,那么客户端在接收到这个被指定参数值的首 部字段对应的响应报文后,就不能使用缓存。换言之,无参数值的首 部字段可以使用缓存。只能在响应指令中指定该参数。
Cache-Control: no-store
当使用 no-store 指令时,暗示请求(和对应的响应)或响应中包含机密信息。
注:从字面意思上很容易把 no-cache 误解成为不缓存,但事实上 no-cache 代表不缓 存过期的资源,缓存会向源服务器进行有效期确认后处理资源,也许称为 do-notserve-from-cache-without-revalidation 更合适。no-store 才是真正地不进行缓存。
因此,该指令规定缓存不能在本地存储请求或响应的任一部分。
Cache-Control: max-age=604800(单位:秒)
当客户端发送的请求中包含 max-age 指令时,如果判定缓存资源的缓 存时间数值比指定时间的数值更小,那么客户端就不会发起请求,而是直接使用自己本地缓存的资源。 另外,当指定 max-age 值为 0,那么客户端一定会发起一个请求给服务端,服务端会进行资源的校验,决定返回304还是200。
应用 HTTP/1.1 版本的缓存服务器遇到同时存在 Expires 首部字段的情 况时,会优先处理 max-age 指令,而忽略掉 Expires 首部字段。而 HTTP/1.0 版本的缓存服务器的情况却相反,max-age 指令会被忽略。
Cache-Control: s-maxage=604800(单位 :秒)
s-maxage 指令的功能和 max-age 指令的相同,它们的不同点是 smaxage 指令只适用于供多位用户使用的公共缓存服务器 ,如CDN缓存。也就是 说,对于向同一用户重复返回响应的服务器来说,这个指令没有任何 作用。
另外,当使用 s-maxage 指令后,则直接忽略对 Expires 首部字段及 max-age 指令的处理。
cache-control指令流程图
请求头部字段
头部名称 | 说明 |
---|---|
If-Match | 比较ETag是否一致,和响应的ETag字段相对应 |
If-None-Match | 比较ETag是否不一致,和响应的ETag字段相对应 |
If-Modified-Since | 比较资源最后更新时间是否一致,和响应的Last-Modified字段相对应 |
If-Unmodified-Since | 比较资源最后更新时间是否不一致,和响应的Last-Modified字段相对应 |
响应头部字段
头部名称 | 说明 |
---|---|
Etag | 资源匹配信息 |
响应实体头部字段
头部名称 | 说明 |
---|---|
Expires | http1.0的缓存过期时间字段 |
Last-Modified | 资源最后一次修改时间 |
缓存校验字段
ETag
首部字段 ETag 能告知客户端实体标识。它是一种可将资源以字符串 形式做唯一性标识的方式。服务器会为每份资源分配对应的 ETag 值。
另外,当资源更新时,ETag 值也需要更新。生成 ETag 值时,并没有 统一的算法规则,而仅仅是由服务器来分配。
资源被缓存时,就会被分配唯一性标识。例如,当使用中文版的浏览 器访问 http://www.google.com/ 时,就会返回中文版对应的资源,而 使用英文版的浏览器访问时,则会返回英文版对应的资源。两者的 URI 是相同的,所以仅凭 URI 指定缓存的资源是相当困难的。若在下 载过程中出现连接中断、再连接的情况,都会依照 ETag 值来指定资 源。
强 ETag 值和弱 Tag 值
ETag 中有强 ETag 值和弱 ETag 值之分。
强 ETag 值,不论实体发生多么细微的变化都会改变其值。
ETag: "usagi-1234"
弱 ETag 值只用于提示资源是否相同。只有资源发生了根本改变,产 生差异时才会改变 ETag 值。这时,会在字段值最开始处附加 W/。
ETag: W/"usagi-1234"
If-Match
形如 If-xxx 这种样式的请求首部字段,都可称为条件请求。服务器接 收到附带条件的请求后,只有判断指定条件为真时,才会执行请求。
首部字段 If-Match,属附带条件之一,它会告知服务器匹配资源所用 的实体标记(ETag)值。这时的服务器无法使用弱 ETag 值。(请参 照本章有关首部字段 ETag 的说明) 服务器会比对 If-Match 的字段值和资源的 ETag 值,仅当两者一致 时,才会执行请求。反之,则返回状态码 412 Precondition Failed 的响 应。
可以理解为客户端校验当前本地缓存是否在服务器端可用。
还可以使用星号(*)指定 If-Match 的字段值。针对这种情况,服务 器将会忽略 ETag 的值,只要资源存在就处理请求。可以理解为客户端强制请求服务器当前的资源实体。
这个值默认是上一次该资源响应头部的ETag字段值。
If-None-Match
首部字段 If-None-Match 属于附带条件之一。它和首部字段 If-Match 作用相反。用于指定 If-None-Match 字段值的实体标记(ETag)值与 请求资源的 ETag 不一致时,它就告知服务器处理该请求。如果一致,就返回304,表示告知客户端使用自己本地的缓存。
在 GET 或 HEAD 方法中使用首部字段 If-None-Match 可获取最新的资 源。因此,这与使用首部字段 If-Modified-Since 时有些类似。
这个值默认是上一次该资源响应头部的ETag字段值。
Last-modified
Last-Modified: Wed, 23 May 2012 09:59:55 GMT
首部字段 Last-Modified 指明资源最终修改的时间。一般来说,这个 值就是 Request-URI 指定资源被修改的时间。
If-Modified-Since
If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT
首部字段 If-Modified-Since,属附带条件之一,它会告知服务器若 If-Modified-Since 字段值早于资源的更新时间,则希望能处理该请求。 而在指定 If-Modified-Since 字段值的日期时间之后,如果请求的资源 都没有过更新,则返回状态码 304 Not Modified 的响应。
这个值默认是上一次该资源响应实体头部的Last-Modified字段值。
If-Unmodified-Since
If-Unmodified-Since: Thu, 03 Jul 2012 00:00:00 GMT
首部字段 If-Unmodified-Since 和首部字段 If-Modified-Since 的作用相 反。它的作用的是告知服务器,指定的请求资源只有在字段值内指定 的日期时间之后,未发生更新的情况下,才能处理请求。如果在指定 日期时间后发生了更新,则以状态码 412 Precondition Failed 作为响应 返回。
可以理解为客户端校验当前本地缓存是否在服务器端可用。
这个值默认是上一次该资源响应实体头部的Last-Modified字段值。
缓存分类
强缓存
强缓存在客户端和服务器端都会存在。
客户端:客户端在请求资源前,会检查上一次该资源响应头的Cache-Control字段,如果该字段的值为max-age=time(大于0的毫秒数),如果该资源缓存的时间没有过这个时间值,则直接使用本地的缓存,而不像服务器发请求。
服务器端:服务器端在接收到一个请求后,如果该请求的头部Cache-Control字段的值为max-age=time(大于0的毫秒数),如果距离上一次返回资源的时间小于这个毫秒数,那么服务器不会读取新的资源,而是直接返回304,告知客户端使用自己本地上次缓存的资源即可。
注:这两种情况,其实归根结底最后都是使用的客户端本地的资源,服务器没有返回资源实体。这样的好处是节省请求次数或者请求流量,但缺点是,如果在max-age时间内服务器资源有更新,客户端无法得到最新的服务器资源。此时可以通过Ctrl+F5强制刷新(其实就是设置一个Cache-Control:no-cache的请求头),获得最新的服务器资源。
协商缓存(对比缓存)
对比缓存值存在于服务器端。
在没有走强缓存逻辑的情况下,服务器端会进行Last-Modified和Etag的校验,如果校验发现资源未更新,则会返回304,否则会返回新的资源实体。