HTTP Cache 的实现方式
对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对 「Request - Response」 的数据缓存在本地,那么下次再次发起相同的请求,就不必要再通过网络获取服务器的响应了,很显然,这将大幅提升性能。
因此,避免发送 HTTP 请求的方法就是通过缓存技术,在 HTTP 协议头中就有不少是针对于缓存的字段。
HTTP 缓存的实现方式:
- 强制缓存
- 协商缓存
强制缓存
强制缓存指的是浏览器只要判断缓存没有过期,则直接使用浏览器的本地缓存,即决定是否使用缓存的主动性在于浏览器这边。
【🌰栗子】下图中,返回的是 200 状态码,但在 状态代码(Status Code) 项中标识的是 来自内存缓存(from disk cache),就是使用了强制缓存。
强制缓存是利用了两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:
Cache-Control
,相对时间Expires
,绝对时间
如果 HTTP 响应头部同时有
Cache-Control
和Expires
字段的话,Cache-Control 的优先级高于 Expires 。
相比较而言,Cache-control
选项更多一些,能进行更加精细的设置,因此建议使用 Cache-Control
来实现强制缓存。具体的实现流程如下:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在响应头部加上
Cache-Control
,Cache-Control
中设置了过期时间大小; - 浏览器再次请求访问服务器中的资源时,会先通过请求资源的时间和 Cache-Control 中设置的过期时间进行对比,来判断该资源是否过期,如果没有,使用缓存,否则重新请求服务器;
- 服务器再次收到请求后,会再次更新响应头部的 Cache-Control。
协商缓存
在使用浏览器开发者工具(F12)时,某些请求的响应码会是
304 Not Modified
,这个是告诉浏览器可以使用本地缓存的资源,通过将这种通过服务器告知客户端时候可以使用缓存的方式被称为协商缓存。
协商缓存的过程
协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存。
协商缓存的实现
协商缓存可以基于两种头部字段来实现:
1)请求头部中的 If-Modified-Since
字段与响应头部中的
Last-Modified
字段实现,这两个字段的意思是:
- 响应头部中的
Last-Modified
:表示这个响应资源的最后修改时间; - 请求头部中的
If-Modified-Since
:当资源过期了,浏览器发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新,说明资源又被改过,则返回最新资源,HTTP 200 OK
;如果最后修改时间较旧,说明资源无新修改,响应HTTP 304
走缓存。
2)请求头部中的 If-None-Match
字段与响应头部中的
ETag
字段,这两个字段的意思是:
- 响应头部中的
Etag
:唯一标识响应资源; - 请求头部中的
If-None-Match
:当资源过期时,浏览器发现响应头中有 Etag,则再次向服务器发起请求时,会见请求头 If-None-Match 的值设置为 Etag 的值。服务器接收到请求后进行对比 Etag,如果资源没有变化则返回 304,如果资源变化了则返回 200。
第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说基于唯一标识的方式可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。
如果在第一次请求资源的时候,服务端返回的 HTTP 响应头部同时有 Etag 和 Last-Modified 字段,那么客户端再下一次请求的时候,如果带上了 ETag 和 Last-Modified 字段信息给服务端,此时 Etag 的优先级更高,也就是服务端先会判断 Etag 是否变化了,如果 Etag 有变化就不用在判断 Last-Modified 了,如果 Etag 没有变化,然后再看 Last-Modified。
为什么 Etag 相较于 Last-Modified,优先级更高?
因为 Etag 能解决 Last-Modified 几个比较难以解决的问题:
- 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求;
- 可能有些文件是在秒级以内修改的,
If-Modified-Since
能检查到的粒度是秒级的,使用 Etag就能够保证这种需求下客户端在 1 秒内能刷新多次; - 有些服务器不能精确获取文件的最后修改时间。
⚠️注意:协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。
小结
「强制缓存和协商缓存的工作流程」
使用 Etag
字段实现的协商缓存的过程:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在响应头部加上 Etag 唯一标识(这个唯一标识的值是根据当前请求的资源生成的);
- 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期:
- 如果没有过期,则直接使用本地缓存;
- 如果缓存过期了,会在请求头部加上
If-None-Match
字段,该字段的值就是 Etag 唯一标识;
- 服务器再次收到请求后,会根据请求中的 If-None-Match
值与当前请求的资源生成的唯一标识进行比较:
- 如果值相等,则返回 304 Not Modified,不会返回资源;
- 如果不相等,则返回 200 状态码和返回资源,并在响应头部加上新的 Etag 唯一标识;
- 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。