浏览器缓存--1

  上一篇文章中,我们知道了报文主体是HTTP报文真正存放数据的地方,可以存放任意的二进制数据,包括图片,文件,字符数据等。现在,我们想像一下,一个客户端多次重复访问一个原始服务器页面时,服务器会多次重复传输同一份资源。一些相同的字节会在网络中一遍遍地传输。这些冗余的数据传输会降低传输速度,加重Web服务器的负载。所以,出现了Web缓存技术。

一:Web缓存技术。

  Web缓存是可以自动保存常见文档副本的HTTP设备。当Web请求抵达缓存时,如果本地有”已缓存”的副本,就可以从本地存储设备而不是原始服务器中提取这个文档。最常见的Web缓存有浏览器缓存和代理服务器缓存。本文主要讨论浏览器缓存。

  浏览器会在用户磁盘上对请求过的文档进行储存,当访问者再次请求这个资源时,浏览器可以从本地磁盘直接获取到资源。

  但现在的问题是,原始服务器上的资源有可能已经发生了变化。所以,HTTP提供了一个机制实现了在尽量少的对服务器的数据请求的前提下保证数据的”新鲜度”。

二:指定资源的过期日期。( Expires 和 Cache-Control )

  服务器用HTTP/1.0+ 的 Expires 首部 或者 HTTP/1.1的Cache-Control:max-age 响应首部来指定过期日期。Expires首部和Cache-Control:max-age 首部本质上是一样的,但由于Cache-Control首部使用的是相对时间而不是绝对日期,所以我们更倾向于使用比较新的Cache-Control首部。( Cache-Control的优先级高于Expires )

我们在服务器告知浏览器120秒后对应的请求过期
response.setHeader("Control-Cache","max-age=120"); #
#对应的响应首部字段:
Cache-Control:"max-age=120"

#在120秒之内,对同一资源的请求将直接从浏览器缓存中取得,对应的HTTP状态码如下:
#表明对此资源的请求成功,但 是直接获取的缓存。
HTTP/1.1 200(from cache) OK    

二:服务器再验证。

  仅仅是已缓存文档过期了并不意味着它和原始服务器上目前处于活跃状态的文档有实际的区别;这只是意味着到了要进行核对的时间了。这种情况被称为“服务器再验证”,说明缓存需要询问原始服务器文档是否发生了变化。

  如果验证显示内容发生了变化,缓存会获得一份新的文档并被浏览器重新缓存。

  如果验证显示内容没有发生变化,缓存只需要获取新的首部,包括一个新的过期日期,并对缓存中的首部进行更新就行了。

  这是一个很棒的系统。缓存并不一定要为每条请求验证文档的有效性-只有在文档过期时它才需要与服务器进行在验证。

 HTTP提供的 条件请求 缓存再验证:

  HTTP的条件方法可以很高效的实现再验证。HTTP允许缓存向原始服务器发送一个“条件GET”,请求服务器只有在源资源与缓存中的副本资源不同时,才响应此请求返回新的资源,否则,直接返回一个不包含实体数据的 304 Modified 状态码报文以告知即使这份文件”过期了”,但内容并没有发生变化,可以继续使用缓存。

  通过这种方式,将验证和对象获取结合成了单个添加GET。向GET请求报文中添加一些特殊的条件首部,就可以发起条件GET。只有条件为真时,Web服务器才会返回实体数据。

  HTTP定义了5个条件请求首部。对缓存再验证来说最有用的2个首部是If-Modified-Since和If-None-Match。所有的条件首部都以前缀 “If-”开头。

 1:If-Modified-Since:

  在进行缓存再验证时,”条件GET”会有一条 If-Modified-Since 的首部字段,其值一般为上一次请求此资源时得到的 Last-Modified 的值。

If-Modified-Since: Tue, 26 Apr 2016 13:22:25 GMT

  服务器会拿着这个日期与要请求的资源的最后修改日期对比,

  如果自指定日期后,文档被修改了,If-Modified-Since 条件就为真,则GET就会成功执行。携带新首部的新文档会被返回给缓存。如果比对发现文档没有被修改过,条件就为假,会向客户端返回一个小的不包含响应实体的 304 Not Modified 响应报文。

 2:If-None-Modified:

  然而,有些情况下仅使用最后修改日期进行再验证是不够的。有些服务器提供的文档会在亚秒间隙发生变化,而使用Last-Modified-Since只是以秒为最小粒度的。为了解决这个问题,HTTP引入了实体标签再验证。

  当客户端初次请求一个资源时,会在响应字段中附加上此文档的实体标签(ETag)。首部字段ETag是一种可将资源以字符串形式作为唯一性标识的方式。

 ETag: "a8df4773ac9fd11:121f"

  这样,当缓存进行再验证时,会附加 If-None-Match: 条件请求。

If-None-Match:"a8df4773ac9fd11:121f"

  同理,如果服务器上的实体标记与 If-None-Modified 的值(缓存中资源的ETag)不一样,说明资源已经改变,就会执行 200 响应并发送新的内容和新的ETag;反之直接返回304。

总结:

  现在,涉及到缓存控制的首部字段有:Expires 和 Cache-Control:max-age ,If-Modified-Since ( 与Last-Modified配合使用 ) 和 If-None-Match ( 与ETag配合使用 )。

  我们来大致总结一下:当我们请求一个资源时:

  1.首先查看浏览器有没有此资源的缓存,如果没有,请求服务器。如果有,根据Expires和Cache-Control:max-age 判断此缓存有没有过期,如果没有过期,直接使用,完全不走服务器。如果已过期,进行服务器的再验证。

  2.第一步中当根据Expires和Cache-Control:max-age 得知资源确实过期后,浏览器会带者 If-Last-Modified 和 If-None-Modified 进入服务器进行再验证。如果两者条件都满足,说明缓存的资源即使过期了但还是“新鲜的”,直接返回304。反之,接收请求并返回新的资源。