内容来源:小林coding
Http缓存有哪些实现方式
对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定会肉眼可见的提升。
所以,避免发送 HTTP 请求的方法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的头部有不少是针对缓存的字段。
HTTP 缓存有两种实现方式,分别是强制缓存和协商缓存
什么是强制缓存
强缓存:
只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边
from disk cache
资源在客户端的有效期(相对时间Cache-Control或绝对时间Expires)
强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:
- Cache-Control,是一个相对时间,例如xxx=3600(也就是一小时后过期);
- Expires,是一个绝对时间,写的是具体的过期时间,简单易用但优先级低;
如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires
Cache-control 选项更多一些,设置更加强制,所以建议使用 Cache-Control 来实现强制缓存,具体的实现流程如下:
1.当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间大小;
2.浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用缓存,否则重新请求服务器;
3.服务器再次收到请求后,会再更新 Response 头部的 Cache-Control
什么是协商缓存
当我们在浏览器使用开发者工具的时候,你可能会看到某些请求的响应码是 304,这个是告诉浏览器可以使用本地缓存的资源
这种通过服务器告知客户端是否可以使用缓存的方式被称为协商缓存
这就是一个协商缓存的过程,所以协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存
两种头部实现
协商缓存可以基于两种头部来实现
请求头部中的 If-Modified-Since 字段(资源是否改变)与响应头部中的 Last-Modified 字段(资源最后修改时间)实现
1.响应头部中的 Last-Modified:标示这个响应资源的最后修改时间
2.请求头部中的 If-Modified-Since:当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间
服务器收到请求后发现有 If-Modified-Since 则与该请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;
如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存
(也就是我们对比资源最后修改时间Last-Modified,如果时间没变那么说明我们的资源没有更新,我们就走缓存)
请求头部中的 If-None-Match 字段(资源是否改变)与响应头部中的 ETag 字段
1.响应头部中的 ETag:唯一标识响应资源;
2.请求头部中的 If-None-Match:当资源过期时,浏览器发现响应头里有 Etag,则再次向服务器发起请求时,会将请求头 If-None-Match 值设置为 Etag 的值。服务器收到请求后进行Etag的比对。如果资源没有变化(ETag值相同)返回 304,如果资源变化了(ETag值不相同)返回 200
优先级
第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间戳导致的不可靠问题。
如果在第一次请求资源的时候,服务器返回回的 HTTP 响应头部同时有 Etag 和 Last-Modified 字段,那么客户端再下一次请求的时候,如果带上了 Etag 和 Last-Modified 字段信息给服务器,这时 Etag 的优先级更高,也就是服务器先判断 Etag 是否变化了,如果 Etag 有变化就不用再判断 Last-Modified 了,如果 Etag 没有变化,然后再看 Last-Modified。
为什么 ETag 的优先级更高?
这是因为 ETag 主要能解决 Last-Modified 几个比较难以解决的问题:
- 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为文件被改动了,从而重新请求;
- 可能修改文件是在秒级以内的,If-Modified-Since 能检查到的粒度是秒级的,使用 Etag 就能够保证这种需求下客户端 1 秒内能刷新多次;
- 有些服务器不能精确获取文件的最后修改时间。
注意,协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候(也就是强制缓存过期),才能发起带有协商缓存字段的请求
总结
强制缓存:
强制缓存中,我们的Cache-Control相对时间的优先级比我们的Expires绝对时间高,因为它选项更多
而且协商缓存必须配合Cache-Control相对时间使用,如果就Expires绝对时间就不能用协商缓存
协商缓存:
If-Modify-Since资源是否改变+客户端Last-Modify资源最后修改时间,通过向服务器看看资源最后修改时间是否有变来判断资源是否过期
If-None-Match资源是否改变+Etag文件唯一标识,通过向服务器看看Etag是否改变来判断资源是否改变
为什么Etag比Last-Modify优先级高,因为Etag是一个标识符,Last-Modify是一个时间戳,时间戳依赖于机器,不同机器的时间戳精确度不同,所以不准确
按照准确度来说,还是Etag标识符好,因为标识符改变了就会改变,不会出现时间戳带来的精度问题