HTTP的缓存协议
浏览器的缓存机制指的是通过在一段时间内保留已接收到的web资源副本,如果在资源的有效时间内,发起了对这个资源的再一次请求,那么浏览器会直接使用缓存的副本,而不是向服务器发起请求。使用web缓存可以有效地提高页面的打开速度,减少不必要的网络带宽的消耗。
浏览器的缓存策略一般由服务器来指定,可以分为两种,分别是强缓存策略和协商缓存策略。
强缓存
使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。强缓存策略可以通过两种方式来设置,分别是http头信息中的Expires属性和Cache-Control属性。
Expires
Expires是http1.0的字段,用来表示资源的过期时间,它是一个绝对时间(当前时间+缓存时间)。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。
但Expires存在这样的问题,用户修改客户端时间,或者由于时差等因素造成的客户端和服务端时间不一致,会导致缓存失效。
Cache-Control
为了解决Expires的缺点,在http1.1中新增了一个属性就是Cache-Control属性,它提供了对资源缓存更精确的控制。它有很多不同的值,比如我们可以通过设置max-age来指定资源能够被缓存的时间大小,这是一个相对的时间,它会根据这个时间的大小和资源第一次请求时的时间来计算出资源过期的时间,因此相对于Expires来说,这种方式更加有效一些。
Cache-Control常用的值还有:
- private,用来规定资源只能被客户端缓存,不能够代理服务器所缓存
- no-store,用来指定资源不能够被缓存
- no-cache,代表该资源能够被缓存,但是立即失效,每次都需要向服务器发起请求。
一般来说只需要设置其中一种方式就可以实现强缓存策略,当两种方式一起使用时,Cache-Control的优先级要高于Expires。
协商缓存
使用协商缓存策略时,浏览器会先向服务器发送一个请求,如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本。
如果资源发生了修改,则返回修改后的资源。协商缓存也可以通过两种方式来设置,分别是http头信息中的Etag属性和Last-Modified属性。
Last-Modified
服务器通过在响应头中添加Last-Modified属性来指出资源最后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加一个If-Modified-Since的属性,属性值为上一次资源返回时的Last-Modified的值。当请求发送到服务器后服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回304状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。
使用这种方法有一个缺点,就是Last-Modified标注的最后修改时间只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,那么文件已经改变了,但是 Last-Modified 却没有改变,这样会造成缓存命中的不准确。
Etag
因为Last-Modified的这种可能发生的不准确性,http中提供了另外一种方式,那就是Etag属性。服务器在返回资源的时候,在头信息中添加了Etag属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个If-None-Match属性,这个属性的值就是上次返回的资源的Etag的值。服务接收到请求后会根据这个值来和资源当前的Etag的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。通过这种方式,比Last-Modified的方式更加精确。
当Last-Modified和Etag属性同时出现的时候,Etag 的优先级更高。使用协商缓存的时候,服务器需要考虑负载均衡的问题,因此多个服务器上资源的Last-Modified应该保持一致,因为每个服务器上Etag的值都不一样,因此在考虑负载均衡时,最好不要设置Etag属性。
总结
强缓存和协商缓存在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。
在实际的缓存机制中,强缓存和协商缓存是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则服务器返回最新的资源给浏览器。
完整的交互流程如下:
