什么是缓存
是应用程序中很重要的一个概念,在有大量数据交换的应用程序中,我们会采取一些方式将那些实时性要求不高的数据生成副本并存储在某个相对来说可快速到达、访问、获取的仓库,这样在需要这些数据的时候我们直接从这个仓库中获取数据。
缓存带来的好处
- 提升数据交换的性能(速度)
- 缓解服务器或数据库的压力
缓存策略
HTTP缓存分为强缓存和协商缓存两种,其中强缓存的优先级最高,在浏览器没有命中强缓存的情况下,会选择协商缓存。注意:强缓存和协商缓存的资源文件全部取自本地缓存。
强缓存
- 不会发送http请求到服务器,状态码200, 缓存空间为 memory cache(内存存储, 浏览器或页面标签关闭后内存中的缓存就会被释放) 或者 disk cache(硬盘存储, 浏览器或页面标签关闭后硬盘中的缓存不会消失,下次进入页面还能从硬盘中获取)
- 根据两个字段判断是否命中缓存,分别是 expires(http1.0) 和 cache-control (http1.1,优先级大于expires), 为了向下兼容,两者一般同时设置
expires: -1 | 绝对时间(Thu Jul 16 2020 14:22:59 GM)
首次加载,响应头包含此字段,下次访问,服务器会对比本地时间和expires,这个资源在这个时间点之前都可以直接从缓存中获取。 expires依赖于本地时间,如果服务端与客户端设置的时间不同,或者手动改变了本地时间,就会出现问题,不能达到预期的效果。
// @1 只能设置绝对时间或者 -1 (永不过期),服务端设置,客户端判断是否过期拿本地时间对
// 比,本地时间不准则缓存失效,故被新版 http 废弃。
// @2 优先级低
const 10sAfter = new Date(Date.now() + 10 * 1000).toGMTString();
res.setHeader('Expires', 10sAfter); // 10s 不要再访问
cache-control: public, max-age=7200
这是一个相对时间(单位:秒),这里代表资源的缓存在这个请求之后的2小时内都有效。
// @1 相对时间,更安全
// @2 优先级高
// @3 Cache-Control: no-store 时,没有缓存(强缓 + 协商缓),浏览器不缓存
// @4 Cache-Control: no-cache 时,不走强缓,直接协商缓存,协商判断有缓存才采用浏览器缓存
// @5 public:可以被任何人缓存,比如中间代理、CDN 等
// @6 private:只能被浏览器缓存
res.setHeader('Cache-Control', 'max-age=10, public'); // 10s 不要再访问
协商缓存(对比缓存)
- 会发送http请求到服务器,如果服务器文件并没有改变,本地缓存空间的文件仍然可用, 此时状态码为 304(not modified),否则状态码为 200,重新拉取文件。
- 根据两对字段判断是否命中缓存,分别是Last-Modified / If-Modified-Since 和 Etag / If-None-Match (优先级高)
Last-Modified / If-Modified-Since:绝对时间(Thu Jul 16 2020 14:22:59 GM)
第一次访问一个资源的时候,服务器会在response header中返回一个Last-Modified,代表这个资源最后的修改时间,当浏览器再次访问这个资源的时候,会在request header中带上 If-Modified-Since,值为上次请求时服务器返回的 Last-Modified 的值,然后服务器根据资源上次修改的时间确认资源在这段期间内是否更改过,如果没有,则返回304,如果有,则返回200并返回最新的资源。但是这样会有个问题,如果资源的修改时间变了,但是内容却没变,这是再利用Last-Modified来计算是否需要重新响应这无疑会带来资源浪费,所以Etag诞生了
Etag / If-None-Match:'5b506e03-25856'
Etag是通过一个校验码来对比资源是否更改过的,而不是通过资源的修改时间。当一个资源修改时,其校验码也会更改。当浏览器请求资源时,服务器会返回一个Etag字段,然后浏览器下一次请求时,会带上 If-None-Match ,值为上次服务器返回的Etag的值,服务器经过校验码的对比后决定返回200或304。
Etag和Last-Modified优先级
Etag可以解决 Last-Modified 不太好处理的问题,Etag能更准确地控制缓存,因此,如果http请求中若同时出现Etag和Last-Modified,Etag的优先级是高于 Last-Modified 的。具体地说,Last-Modified 有以下一些问题:
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
- 某些服务器不能精确的得到文件的最后修改时间。
拓展: