HTTP缓存
我们知道HTTP的缓存属于客户端缓存,后面会提到为什么属于客户端缓存。所以我们认为浏览器存在一个缓存数据库,用于储存一些不经常变化的静态文件(图片、css、js等)。我们将缓存分为强制缓存和协商缓存。下面我将分别详细的介绍这两种缓存的缓存规则
两类缓存机制可以同时存在,强制缓存的优先级高于协商缓存,当执行强制缓存时,如若缓存命中,则直接使用缓存数据库数据,不在进行缓存协商
强缓存
可以理解为无须验证的缓存策略。对强缓存来说,响应头中有两个字段 Expires/Cache-Control 来表明规则。
Expires
Expires 指缓存过期的时间,超过了这个时间点就代表资源过期。有一个问题是由于使用具体时间,如果时间表示出错或者没有转换到正确的时区都可能造成缓存生命周期出错。并且 Expires 是 HTTP/1.0 的标准,现在更倾向于用 HTTP/1.1 中定义的 Cache-Control。两个同时存在时也是 Cache-Control 的优先级更高。
Cache-Control
Cache-Control 可以由多个字段组合而成,主要有以下几个取值:
1.max-age指定一个时间长度,在这个时间段内缓存是有效的,单位是s。在没有禁用缓存并且没有超过有效时间的情况下,再次访问这个资源就命中了缓存,不会向服务器请求资源而是直接从浏览器缓存中取。
2.s-maxage同 max-age,覆盖 max-age、Expires,但仅适用于共享缓存,在私有缓存中被忽略。
3.public表明响应可以被任何对象(发送请求的客户端、代理服务器等等)缓存。
4.private表明响应只能被单个用户(可能是操作系统用户、浏览器用户)缓存,是非共享的,不能被代理服务器缓存。
5.no-cache强制所有缓存了该响应的用户,在使用已缓存的数据前,发送带验证器的请求到服务器。不是字面意思上的不缓存。
6.no-store禁止缓存,每次请求都要向服务器重新获取数据。
协商缓存
缓存的资源到期了,并不意味着资源内容发生了改变,如果和服务器上的资源没有差异,实际上没有必要再次请求。客户端和服务器端通过某种验证机制验证当前请求资源是否可以使用缓存。
浏览器第一次请求数据之后会将数据和响应头部的缓存标识存储起来。再次请求时会带上存储的头部字段,服务器端验证是否可用。如果返回 304 Not Modified,代表资源没有发生改变可以使用缓存的数据,获取新的过期时间。反之返回 200 就相当于重新请求了一遍资源并替换旧资源。
Last-modified/If-Modified-Since
Last-modified: 服务器端资源的最后修改时间,响应头部会带上这个标识。第一次请求之后,浏览器记录这个时间,再次请求时,请求头部带上 If-Modified-Since 即为之前记录下的时间。服务器端收到带 If-Modified-Since 的请求后会去和资源的最后修改时间对比。若修改过就返回最新资源,状态码 200,若没有修改过则返回 304。
从字面上看,就是说:从某个时间节点算起,是否文件被修改了
如果真的被修改:那么开始传输响应一个整体,服务器返回:200 OK
如果没有被修改:那么只需传输响应header,服务器返回:304 Not Modified
注意:如果响应头中有 Last-modified 而没有 Expire 或 Cache-Control 时,浏览器会有自己的算法来推算出一个时间缓存该文件多久,不同浏览器得出的时间不一样,所以 Last-modified 要记得配合 Expires/Cache-Control 使用。
Last-Modified 说好却也不是特别好,因为如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为Last-Modified时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。为了解决这个问题,HTTP1.1推出了Etag。
Etag/If-None-Match
由服务器端上生成的一段 hash 字符串,第一次请求时响应头带上 ETag: abcd,之后的请求中带上 If-None-Match: abcd,服务器检查 ETag,
如果不同,说明资源被改动过,则响应整个资源内容,返回状态码200。
相同,说明资源无心修改,则响应header,浏览器直接从缓存中获取数据信息。返回状态码304.
但是实际应用中由于Etag的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用Etag了。
关于 last-modified 和 Etag 区别,已经有很多人总结过了:
某些服务器不能精确得到资源的最后修改时间,这样就无法通过最后修改时间判断资源是否更新。
Last-modified 只能精确到秒。
一些资源的最后修改时间改变了,但是内容没改变,使用 Last-modified 看不出内容没有改变。
Etag 的精度比 Last-modified 高,属于强验证,要求资源字节级别的一致,优先级高。如果服务器端有提供 ETag 的话,必须先对 ETag 进行 Conditional Request。
注意:实际使用 ETag/Last-modified 要注意保持一致性,做负载均衡和反向代理的话可能会出现不一致的情况。计算 ETag 也是需要占用资源的,如果修改不是过于频繁,看自己的需求用 Cache-Control 是否可以满足。
所以,在我们请求一个资源的时候,会先根据http头部的experis和cache-control来判断是否命中强制缓存,若命中则使用缓存的数据,此时不会向服务端发送网络请求。没有命中,则会向服务端请求数据。同时进行协商缓存的命中。检查Last-modified/If-Modified-Since和Etag/If-None-Match,若命中则使用缓存,未命中,则从服务端返回资源