HTTP缓存
HTTP缓存
HTTP缓存实现有两种强制缓存和协商缓存
强制缓存
强缓存:浏览器判断请求的目标资源是否有效命中强缓存,如果命中,则直接从内存中读取目标资源,无需与服务器做任何通讯
- Expires强缓存:设置一个强缓存时间,此时间范围内,从内存中读取缓存并返回,判断强缓存过期的机制是获取本地时间戳,与之前拿到的资源文件中的Expires字段的时间做比较。
- Cache-Control强缓存 : http1.1 中增加该字段,只要在资源的响应头上写上需要缓存多久就好了,单位是秒。 Cache-Control:max-age=N , 有max-age、s-maxage、no-cache、no-store、private、public这六个属性。
max-age 决定客户端资源被缓存多久。
s-maxage 决定代理服务器缓存的时⻓。
no-cache 表示是强制进⾏协商缓存。
no-store 是表示禁⽌任何缓存策略。
public 表示资源既可以被浏览器缓存也可以被代理服务器缓存。
private 表示资源只能被浏览器缓存,默认为private
协商缓存
当客户端发送一个请求时,包含了一些与缓存相关的头部信息,例如"If-Modified-Since"和"If-None-Match"等。这些头部信息告诉服务器客户端上次获取资源的时间或者资源的唯一标识符(通常是ETag)。服务器接收到请求后,会检查这些头部信息,并与资源的当前状态进行比较。
如果资源没有发生变化,服务器可以返回一个特殊的响应码,如304 Not Modified。这个响应告诉客户端可以使用之前缓存的副本,而无需重新下载整个资源。这样可以减少网络传输的数据量,提高性能和效率。
如果资源已经发生了变化,服务器会返回最新的资源内容以及新的缓存相关的头部信息,客户端将根据这些信息更新缓存。
1、基于Last-Modified和If-Modified-Since的协商缓存
流程:
- 首先需要在服务器端读出文件修改时间
- 将读出来的修改时间赋给响应头的Last-Modified字段
- 最后设置Cache-control:no-cache
- 当客户端读取到Last-Modified的时候,会在下次的请求标头中携带一个字段:If-Modified-Since,而这个请求头中的If-Modified-Since就是服务器第一次修改时候给他的时间
- 之后每次对该资源的请求,都会带上 If-Modified-Since 这个字段,⽽服务端就需要拿到这个时间并再次读取该资源的修改时间,让他们两个做⼀个⽐对来决定是读取缓存还是返回新的资源
- 如果最后修改时间较新(⼤),说明资源⼜被改过,则返回最新资源, HTTP 200 OK 如果最后修改时间较旧(⼩),说明资源⽆新修改,响应 HTTP 304 ⾛缓存。
缺点:
- 因为是根据⽂件修改时间来判断的,所以,在⽂件内容本身不修改的情况下,依然有可能更新⽂件修改时间(⽐如修改⽂件名再改回来),这样,就有可能⽂件内容明明没有修改,但是缓存依然失效了。
- 当⽂件在极短时间内完成修改的时候(⽐如⼏百毫秒)。因为⽂件修改时间记录的最⼩单位是秒,所以,如果⽂件在⼏百毫秒内完成修改的话,⽂件修改时间不会改变,这样,即使⽂件内容修改了,依然不会返回新的⽂件。
2、基于ETag的协商缓存:将原先协商缓存比较时间戳的形式改成了比较文件指纹(根据文件内容计算出唯一的哈希值)
- 第一次请求某资源的时候,服务器读取文件并计算出文件指纹,将文件指纹放在响应头的Etag中和资源一起返回给客户端
- 第⼆次请求某资源的时候,客户端⾃动从缓存中读取出上⼀次服务端返回的 ETag 也就是⽂件指纹。并赋给请求头的 If-None-Match 字段,让上⼀次的⽂件指纹跟随请求⼀起回到服务端。
- 服务端拿到请求头中的 If-None-Match 字段值(也就是上⼀次的⽂件指纹),并再次读取⽬标资源并⽣成⽂件指纹,两个指纹做对⽐。如果两个⽂件指纹完全吻合,说明⽂件没有被改变,则直接返回 304 状态码和⼀个空的响应体并return。如果两个⽂件指纹不吻合,则说明⽂件被更改,那么将新的⽂件指纹重新存储到响应头的ETag中并返回给客户端
缺点:
- ETag的服务端需要更多的计算开销
- ETag有强验证和弱验证,所谓将强验证,ETag⽣成的哈希码深⼊到每个字节。哪怕⽂件中只有⼀个字节改变了,也会⽣成不同的哈希值,它可以保证⽂件内容绝对的不变。但是,强验证⾮常消耗计算量。ETag还有⼀个弱验证,弱验证是提取⽂件的部分属性来⽣成哈希值。因为不必精确到每个字节,所以他的整体速度会⽐强验证快,但是准确率不⾼。会降低协商缓存的有效性。