HTTP 缓存
和计算机存储体系一样,web中也存在着缓存,这些缓存可以让人们不必每次都访问地域上很遥远的web服务器,缓存的存在大大地减缓了网络拥塞。
HTTP 协议中就存在一些首部用于控制缓存,下面一一罗列并讲解:
缓存控制
Cache-Control:max-age=1000
max-age 定义了文档的最大使用期,从第一次生成文档到文档不再新鲜最大合法存在时间,它是相对于文档的创建时间来说的,单位为秒。
Expires:Fri,05 Jul 2002,05:00:00 GMT
指定一个具体的时间,这个时间之后文档就不再有效了。由于客户端设备上的系统时间可能有错误,所以有可能出现意外。
再验证
如果已经缓存的文档过期了,这个时候也不意味着它的内容已经发生了变化,这个时候缓存会向服务器发起再验证,缓存会获取一份这个文档新的副本,如果文档没有更新,那么缓存就将这个数据发回客户端,并更新相应的首部信息,包括新的过期时间。
但是如果从原服务器获取文档失败了,那么就不能发送已经过期的缓存了,而是发送错误报文。
用条件方法进行再验证
- If-Modified-Since:
如果从指定日期后文档被修改了,就执行请求。如果从指定日期后文档没有被修改过,那就会返回一个304 Not Modified 响应,这个时候缓存一般之后发送一些变更过的头部信息。否则就会返回一个200 OK 响应。
If-None-Match:<tags>
有些时候仅仅使用最后修改时间来验证是不够的,因为有时候经管文档被修改了,但是修改并不重要到需要全球范围内缓存进行重装,或者尽管修改了,但是内容并没有变化(重写了文档),还有的服务器不能得到文档最后修改时间。为了解决这些问题 HTTP 允许对被称为 实体标签(ETag) 的版本标识符来比较。实体标签是附加到文档上的任意的标签。他们可能包含的是文档的版本号或者是序列号等。当发布者修改了文档后,可以修改这些实体标签来说明这是一个新的版本。这样缓存就可以使用 If-None-Match 条件首部来获取文档的新副本了。
// request
GET /about.html HTTP/1.0
If-None-Match:"v2.6","v2.5","v2.4"
// response
HTTP/1.0 304 Not Modified
ETag:"v2.6"
何时使用实体标签,何时该使用最后修改时间
如果服务器回送了一个实体标签,那么客户端就必须使用实体标签进行验证。如果服务器只回送了 Last-Modified 客户端就可以使用 If-Not-Midified 来验证。
如果服务器收到的请求既有If-Modified-Since 又有实体标签,那么只有两者都满足,才会回送 304 响应。
控制缓存的能力
Cache-Control:no-store|no-cache|must-revalidate|max-age
no-store
标识为 no-store 的响应,是不会进行缓存的,缓存就像非缓存代理一样向客户端转发该相应,然后删除该对象。
no-cache
标识为 no-cache 的响应,并非不会存储在缓存中,只是在与原服务器进行新鲜度验证之前,缓存是不能将其提供给客户端的。也就是说,每次访问该文档,都会进行新鲜度验证。
max-age
表示从服务器将文档传来时起,具有多少秒的新鲜时间。
must-revalidate
…
试探性过期
如果响应首部中没有 expires 和 Cache-Control:max-age 首部,那么缓存就会自己估计一个时间。可能会根据文档的最后修改时间来估计。最近修改的文档很有可能会再次修改,而很久以前修改过的文档很有可能是一份稳定的文档,因此缓存时间可能会较长。
客户端的新鲜度限制
对于用户点击 refresh 按钮这样的行为,是会无条件地从原始服务器中获取文档。当然在请求中头中也可以添加 Cache-Control 来限制文档的新鲜度。
Cache-Control:max-stale=<s>|min-fresh=<s>|no-cache|max-age|no-store|only-if-cached