(转)HTTP 缓存机制

By | 2017年3月29日

转自:前端学习之路

 

绪论

当web请求到达缓存时,如果本地有”已缓存”的副本,就可以从本地设备而不是原始服务器中提取这个文档

使用缓存的好处

  • 减少了冗余的数据传输
  • 缓解了网络瓶颈的问题
  • 降低了对原始服务器的要求
  • 降低了距离时延

应用缓存时可能会出现的几种情况

命中的和未命中的

可用的已有副本为某些到达缓存的请求提供服务,这被称为缓存命中

其他一些到达缓存的请求可能会由于没有副本可用,而被转发给原始服务器,这被称为缓存未命中

缓存命中与未命中

缓存再验证命中与缓存再验证未命中

下面问题就来了,如果我们一直使用缓存中的文档,如何把服务器上最新的变动告知客户端?

HTTP使用了一些简单的机制,来保持已缓存数据与服务器数据之间的一致性。

通过『新鲜度检测』(也叫做HTTP再验证)可以对服务器上的文档进行检测,从而查看当前缓存的文档是不是服务器上的最新版本。

缓存再验证命中与未命中

HTTP通过在请求首部添加”If-Modified-Since”字段,可以实现再验证的过程。再验证的结果有以下三种可能:

  • 再验证命中:即缓存中的副本与服务器中的文档依然相同,服务器返回304 Not Modified,表示服务器对象未被更改
  • 再验证未命中:即服务器上的文档发生了变动,服务器返回完整的200 OK响应,表示服务器对象已与缓存对象不同,重新从服务器获取对象
  • 对象被删除:服务器返回一个404 Not Found响应,表示服务器上该对象已被删除

下面对缓存机制进行详细说明

缓存机制

cache-flow

过程一

客户端发送一个GET请求到缓存,并判断缓存中是否存在所请求文件的副本。

=== 有,则进入 -> 过程五 ===

=== 没有,则进入 -> 过程二 ===

过程二

当前缓存中没有所请求文件的副本,则将请求转发到服务器。

=== 进入 -> 过程三 ===

过程三

服务器收到请求,将完整的请求内容包含在响应里。

通过特殊的HTTPCache-Control首部和Expires首部,HTTP让服务器向每个文档附加了一个”过期时间”,并将文档存入缓存当中。在文档过期之前,缓存可以任意频率使用这些副本,而无需与服务器联系(除非请求中有限制)。

同时,响应头还包括Last-Modified首部和Etag首部。这两个首部表示此时请求的文件最新一次修改是什么时候(Last-Modified:< date >)和当前的标签是什么(Etag:< tag >)。

cache-control

=== 进入 -> 过程四 ===

过程四

缓存最后将收到的文档提供给客户端

=== OVER ===

过程五

检测缓存中对应文件的过期时间,判断文档是否”新鲜”

=== 新鲜,则进入 -> 过程六 ===

=== 不新鲜,则进入 -> 过程七 ===

过程六

直接将缓存中的文件提供给客户端

=== OVER ===

过程七

用条件方法进行再验证:缓存向原始服务器发送一个”条件GET”

HTTP定义了5个条件请求首部。

  • If-Modified-Since
  • If-Unmodified-Since
  • If-Match
  • If-None-Match
  • If-Range

其中最有用的是If-Modified-SinceIf-None-Match

还记得上文提到的服务器响应中的Last-Modified首部和Etag首部吗?在发送”条件请求的时候”,请求首部中If-Modified-Since的值即为上次在响应中收到的Last-Modified的值,而If-None-Match的值即为上次收到的响应中Etag的值。

举个例子来说明:

假设”今天”是 2017年3月20日,在这一天,服务器上A文件最后的更新日期为 2017年3月18日 00:00。客户端在”今天”向服务器请求A文件,服务端返回内容的同时,在响应头添加了Last-Modified: 2017年3月18日 00:00(注:真正的日期格式应该是这种:Tue, 10 Jan 2017 16:05:44 GMT,这里为了方便进行了简写),客户端收到响应之后表示已经知道这个文件在服务端的最后更改日期是哪天了。下次客户端再发送条件请求的时候,就将If-Modified—Since设置为之前收到的Last-Modified首部的值。通过这种方式就能够检测距离上次请求到现在这一刻位置,所请求对象是否发生改变。

EtagIf-None-Match的使用方法也类似上述过程。

=== 进入 -> 过程八 ===

过程八

服务器将接收到的请求头中的If-Modified-SinceIf-None-Match首部的值取出,并与服务器上对应请求的文档信息进行对比

=== 条件为真,则表示文件已发生变动,进入 -> 过程十一 ===

=== 条件为假,则表示文件没有变化,进入 -> 过程九 ===

过程九

服务端返回 304 Not Modified,并且在响应头中加入新的Cache-Control和/或Expires首部,用来更新缓存上的”新鲜度信息”。

=== 进入 -> 过程十 ===

过程十

将缓存中的文件提供给客户端

=== OVER ===

过程十一

向服务端请求更新后的文件

=== 进入 -> 过程三 ===

其他

Cache-Control

该首部可以设置的值:

max-age=XXXX(设置相对秒数,即还有XXXX秒文件变为”不新鲜”)
s-maxage=XXXX(仅适用于共有缓存)
no-store(禁止缓存对响应进行复制)
no-cache(响应可以存在缓存中,但是每次都要进行再验证)

Expires 和 Cache-Control

Expirese使用绝对日期,但由于许多服务器时钟都不同步,因此不建议使用

Cache-Control则使用相对时间

If-Modified-Since 和 If-None-Match

当请求中同时存在If-Modified-SinceIf-None-Match两种首部,则只有当两者条件都为真的时候,服务器才返回304 Not Modified