缓存的新鲜度判断——HTTP权威指南读书心得(十二)
缓存的新鲜度判断
这回主要来说一说缓存是如何进行新鲜度判断的。上回书说到缓存中的每个资源都有它自己的保质期,我们称之为新鲜度。当资源处于这个保质期之内,那么缓存就认为它是新鲜的,无需进行再次验证就可以发送给客户端。那么这个保质期是怎么定义的呢?而如果当保质期过了之后如何进行在验证呢?
资源过期
通过 Cache-Control首部和expires首部,原始服务器可以对资源定义其保质期。在保质期之内,缓存就认为该资源是新鲜的,可以直接传回给客户端,如果过期那么就需要进行再验证。
Cache-Control首部和Expires首部其实定义的都是资源的过期时间,Cache-Control使用的是相对时间,而expires使用的是绝对时间,绝对时间依赖于计算机时钟,所以建议使用Cache-Control首部。
百度首页这个事务使用的就是Expires首部,而 Cache-Control首部的形式是这样的:
Cache-Control:max-age=484200
这个单位是秒。Expires首部比较好看懂,他就是表示过了这个日期这个资源就过期了。Cache-Control首部则表示从服务器生成该资源开始经过多少秒之后该资源过期。
服务器再验证
资源过期了怎么办?再验证。怎么再验证?为什么要再验证?当缓存中的资源过期了以后,并不代表着这个资源真正的过期了,这个资源服务器有可能在这段时间内没有进行过变更。所以为了验证已经过期的资源是否真的不同了,就要进行再验证。如果再验证显示资源发生了变化,那么缓存就会获取一份新的资源副本。如果再验证显示资源没有发生变化,那么缓存就只获取新的首部,包括一个新的过期日期并对缓存中信息进行更新。
HTTP协议要求缓存正确的返回以下内容之一:
- 足够新鲜的资源副本
- 与服务器进行过再验证,确认其仍然新鲜的资源副本
- 如果需要进行再验证的服务器出现了故障,则返回一条错误报文
- 附有警告信息说明内容可能不正确的资源副本
如何进行缓存验证?
http可以使用条件方法高效的再验证。协议允许缓存向服务器发送一条条件GET,如果资源发生改变,则只返回相关报头。如果资源发生了改变,则在相应报文中直接返回对应的资源对象。HTTP中定义了五个条件请求首部,其中If-Modified-Since和If-None-Match两个首部了。
If-Modified-Since:如果从指定日期之后资源被修改过了,那么就返回新的资源,可以与Last-Modified首部配合使用
If-None-Match:服务器可以为每一个资源定义一个特定的标签,如果说进行再验证的副本资源的标签与当前服务器上的标签不同,则返回新的资源。
最常用的是If-Modified-Since,If-Modified-Since请求一般被称为IMS请求,由字面意思可知,它表明自某个日期之后资源发生了变换之后,服务器才会返回新的资源。如果自指定日期之后,资源被修改了,那么服务器就会返回一条包括新的首部新的过期日期和新的资源的响应报文。如果资源没有被修改过,那么服务器就不会发送资源,只会发送新的报头和过期时间,
If-Modified-Since首部可以与Last-Modified首部配合使用,服务器会将最后修改时间附加到所回送的报文上去。当进行在验证的时候,可以使用If-Modified-Since:(最后修改时间) 这种方式来进行验证,这个就表示看该资源在上一次修改之后有没有发生变化。如果资源没有发生变化的话,服务器就会回送一个304 NOT Modified响应。
有些服务器并没有将If-Modified-Since中的信息当做时间进行比较,而是进行字符串匹配。这种方法验证的是该资源是不是特定时间的资源了。这种方法能很好的识别出缓存是否过期,但是如果客户端想用该时间做一些其他的真正基于时间的工作的话就会出现问题。
有些情况下仅仅使用日期验证是不够的
例如:有些文档可能会进行周期性的重写,但实际上并没有发生变化。有些文档发生了变化,但是这些变化并不重要,不需要缓存对其进行更新等等。遇到这种情况,就需要使用ETag和 If-None-Match进行在验证了。可以把实体标签比喻成资源的版本号,当进行在验证的时候,就把缓存中的ETAG和服务器中资源的ETAG进行比较,看是否满足要求,如果不匹配,那么就回送新的资源。
If-None-Match首部可以包含几个ETag,告诉服务器缓存中已经存在这些实体了。例如:
If-None-Match:“v1.0”,“v1.1”,“v1.2”
强弱验证器
有时候,服务器希望在对文档进行一些非实质性的或者不重要的更改时,不需要使所有的缓存都失效,那么就可以使用弱验证器。例如版本号在v2.6之下的,如v2.63等都看做它是新鲜的。那么如何使用弱验证器呢?例子:
ETag:W/"v1.0"
If-None-Match:W/“v1.0”
(只要加W/就好了。)
原始服务器一定不能为两个不同的实体重用一个特定的强实体标签值,或者为两个语义不同的实体重用一个特定的弱实体标签值,因为副本在缓存内留存的时间不固定,容易发生冲突。
什么时候使用实体标签和最近修改日期呢?
如果服务器回送一个ETag,客户端就必须使用实体标签验证器了,如果服务器回送了一个Last-Modified值,客户端就可以使用 If-Modified-Since验证。如果都提供了的话,那么就都可以使用了。
除非HTTP/1.1原始服务器无法生成实体标签验证器,否则就应该发送一个出去。