协商缓存
所谓“协商”,可以理解为:客户端和服务端双方商量着来。
客户端检查资源超过有效期、强缓存命中失败的情况下,则发出请求“询问”服务器是否资源真的过期了,询问的同时在请求头要携带着资源的「上次更新时间」或者「唯一实体标识」(不同http版本导致的共存问题)。
服务端核对客户端要请求的资源的「上次更新时间」或者「唯一实体标识」:
-
若一致,说明命中协商缓存,只返回304; -
若不一致,说明资源有更新,则返回200、新资源,同时响应头返回「资源修改时间」后者「资源最新的实体标识」。同时,客户端拿到新的资源及其修改时间与标识后,重新进行缓存。
概括如下图:
缓存验证
协商缓存就是缓存验证。
触发时机:
-
用户点击刷新按钮时会开始缓存验证。 -
如果缓存的响应头信息里含有"Cache-control: must-revalidate”的定义,在浏览的过程中也会触发缓存验证。 -
另外,在浏览器偏好设置里设置Advanced->Cache为强制验证缓存也能达到相同的效果。
触发条件:
只有在服务器返回强校验器或者弱校验器时才会进行验证。
附带条件请求
形如 If-xxx 这种样式的请求首部字段,都可称为条件请求。
服务器接 收到附带条件的请求后,只有判断指定条件为真时,才会执行请求。
协商缓存中,就有很多这样的附带条件请求。
《图解HTTP》
协商缓存 特点:
-
服务端校验 -
304 Not Modified状态码 -
Last-Modified -
Etag
304(Not Modified)
该状态码虽然是3XX的类别,但是跟301、302不一样,不是重定向的含义。304,Not Modified。表示服务端资源未改变,可直接使用客户端缓存过的、未过期的资源。
他的触发条件是:
1、客户端采用GET方法,且在请求报文中含有“If-Match”、“If-Modified-Since”、“If-None-Match”、“If-Range”、“If-Unmodified-Since”等字段
2、服务器端接收到请求,允许请求并访问资源。但因客户端的请求未满足条件,就直接返回了304。
304状态码返回时,不包含任何响应的主体部分。
也就是说,如果命中协商缓存,服务端响应请求时,只会返回一个304状态码、并没有实际上的文件内容,因此在响应体体积上的节省是协商缓存的优化点
HTTP/1.0 Last-Modified组
Last-Modified (资源的最后修改日期时间)
实体首部字段:Last-Modified,表示资源最后被修改的时间。
格式如:
last-modified: Thu, 01 Jan 1970 00:00:00 GMT
这句话就像是服务器告诉客户端,你请求的这个文件是1970年1月1日修改的。
Last-Modified是一种缓存弱校验器。说它弱是因为它只能精确到一秒。
如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存:
If-Modified-Since (比较资源的更新时间)
请求首部字段
他是与Last-Modified对应的字段,存储的是上次缓存的资源最终更新时间,也就是上次缓存资源时获取的Last-Modified的值。
用于确认代理服务器/客户端拥有的本地资源的有效性。
如果在If-Modified-Since字段指定的日期时间后,资源发生了改变,服务器会接受请求。
上图中,服务端拿着他的值和服务端本地被请求资源的Last-Modified进行比较:
-
如果Last-Modified <= If-Modified-Since,说明资源一致,命中协商缓存,返回304状态码 Not Modified即可。 -
如果Last-Modified > If-Modified-Since,说明资源被修改,需要返回最新资源给客户端。
他的格式如:
if-modified-since: Thu, 01 Jan 1970 00:00:00 GMT
HTTP/1.1 Etag组
Etag (资源的匹配信息)
响应首部字段,缓存的一种强校验器。
实体标记(Etag)是与特定资源关联的特定值,是资源唯一性标识的字符串。服务器会为每份资源分配对应的 ETag 值。 并通过响应头首部字段告知客户端资源的实体标识。
格式如:
etag: f7b80870fbcd8f9da18ab22d2ef1932c
特点:
-
当资源更新时,ETag 值也需要更新。 -
而且,生成 ETag 值时,并 没有统一的算法规则,而仅仅是由服务器来分配。所以分布式服务器系统,一模一样的一个文件的Etag值可能不一样。 -
此外,因为是按照内容不同来生成的唯一标识,中英文对应版本的资源,虽然地址相同,其Etag不同。
强弱Etag:
ETag 中有强 ETag 值和弱 ETag 值之分。
强ETag值
强 ETag 值,不论实体发生多么细微的变化都会改变其值。
ETag: "usagi-1234"
弱ETag值
弱 ETag 值只用于提示资源是否相同。只有资源发生了根本改变,产 生差异时才会改变 ETag 值。
这时,会在字段值最开始处附加 “W/”。如下:
ETag: W/"usagi-1234"
If-None-Match (比较实体标记)
请求首部字段
他是与Etag对应的字段,存储的是上次缓存的资源的实体标记值,也就是上次缓存资源时获取的Etag的值。
协商缓存时,客户端携带该字段与服务端资源的Etag字段值进行比对,只有在If-None-Match的字段值与Etag值匹配不上、不一致时,命中协商缓存。
在GET或HEAD请求方法中,使用If-None-Match可获取最新的资源。
格式如:
if-none-match: f7b80870fbcd8f9da18ab22d2ef1932c
他和If-Match的作用相反。
If-Match 与412 状态码
用法和规则基本同If-None-Match,但判断逻辑完全相反。
If-Match的这个条件的判断逻辑是:只有当 If-Match 的字段值跟 ETag 值匹配一致时才会命中协商缓存。服务器才会接受请求 并返回200和新数据。
反之,服务器返回状态码 412 Precondition Failed 的响应。
还可以使用 星号(*) 指定 If-Match 的字段值。
针对这种情况,服务器将会忽略 ETag 的值,只要资源存在就处理请求。
if-match: f7b80870fbcd8f9da18ab22d2ef1932c
或者
if-match: *
对比
首先,Etag的优先级高于Last-Modified。
Last-Modified和Etag的优缺点分析如下:
Last-Modified优点
不存在版本问题,每次请求都会去服务器进行校验。服务器对比最后修改的时间,如果相同返回 304,不同的话返回 200 以及相应的数据资源
Last-Modified缺点
-
只要资源修改,无论内容是否发生实质性的变化,都会将该资源返回给客户端。 例如周期性重写,这种情况下该资源包含的数据实际上是一样的; -
以时刻作为标识, 无法识别一秒内进行多次修改的情况。如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒; -
某些服务器 不能精确的得到文件最后修改时间; 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用
Etag优点
-
可以更加 精确的判断资源是否被修改, -
可以识别 一秒内多次修改的情况; -
不存在版本问题,每次请求时都会去服务器进行校验。
Etag缺点
-
计算 Etag 值需要 性能损耗; -
分布式服务器时依赖算法:分布式服务器存储的情况下,计算 Etag 的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时现 Etag 不匹配的情况。
两组字段流程整理如下
最后再整体回顾、复习一下子。
(注意:实际HTTP1.1的请求中,两组字段同时包含在请求及响应头中,我这里为了加深组CP的印象,分开阐述)
1、Last-Modified组整体流程如下:
-
服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间 -
浏览器将这个值和内容一起记录在缓存数据库中 -
下一次请求相同资源的时候,浏览器从自己的缓存中找出"不确定是否过期的"缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-since 字段 -
服务器会将 If-Modified-since 的值与 If-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,表示修改响应 200 状态码并返回数据
2、Etag组整体流程如下:
浏览器在发起请求时,服务器在响应头中返回请求资源的唯一标识。在下一次请求时,会将上一次返回的 Etag 值赋值给 If-None-match 并添加在响应头中。服务器将浏览器传来的 if-no-matched 跟自己的本地的资源的 Etag 做对比,如果匹配,则返回 304 通知浏览器读取本地缓存,否则返回 200 和更新后的资源。