HTTP——缓存

背景&思考

在客户端请求资源时如果每次都向 server 发请求,server 把数据返回给客户端,不仅耗时还有大量的流量浪费。解法是------缓存,第一次请求时客户端将 response 保存起来,下次再请求时直接从缓存取数据,避免重复请求。其中还有很多细节:

  • Q1:server的数据修改后,本地缓存的数据就过期了,每次都从本地取数据有数据不一致的风险。解法是给缓存的数据设置一个保质期,保质期内从缓存取数据,过保质期后请求 server。
  • Q2:过保质期后请求 server,由于 server 的数据不一定会更新,还是有请求浪费的情况。

为解决上述问题,HTTP 协议在缓存策略上做了很多规定,本文章主要围绕上述两个问题讲解 HTTP 的规定。

1、Q1------强制缓存

客户端本地缓存有保质期,如何确定缓存数据是否有效呢?

1.1、Expires(HTTP/1.0)

HTTP 1.0 在 response 的 header 中加 Expires

yaml 复制代码
Expires: Tue, 28 Feb 2022 22:22:22 GMT

Expires:是资源将来的过期时间。下次发起请求时客户端会判断:

  • 当前时间未超过Expires:缓存未过期,从缓存取数据,不发出请求。通过调试模式可以看到状态码后有(from memory or from disk
  • 当前时间未超过Expires:表示缓存过期,会发出请求获取最新数据。

使用 Expires 的问题:

  1. 时间格式难以解析,会引发一些问题。
  2. 如果手动调整设备的时间,缓存策略就不准了。

所以 HTTP 1.1 使用 Cache-Control 指定缓存策略。

1.2、Cache-Control:max-age(HTTP/1.1)

在 response 中设置 Cache-Control,值有:

private :客户端可以缓存
public :客户端和代理服务器均可缓存;
max-age=xxx :缓存的资源将在 xxx 秒后过期;
no-cache :需要使用协商缓存来验证是否过期;
no-store:不可缓存

常用的是 max-age=xxx,意思是 xxx 秒后缓存过期。请求时缓存在有效期内则使用缓存:

超过有效期则会向 server 请求:
如果 ExpiresCache-Control: max-age 都设置了,优先级是:max-age > Expires。由于 HTTP/1.1 已被广泛使用,无需特地提供 Expires

1.3、测试 Demo

这里有个 Nodejs 写的 server Demo,可以使用 HTTPCacheServer 验证缓存策略。
图片 example.png 设置了 max-age=10,10秒内刷新可以看到使用的是缓存:


第二张图片 example1.png 是验证协商缓存。

2、Q2------协商缓存

协商缓存是为了处理强制缓存失效的情况。强制缓存失效后会去请求 server,需要一套规则来告诉客户端缓存是否真的过期。

2.1、Last-Modified/If-Modified-Since(HTTP/1.0)

Last-Modified:资源上次修改的时间。server 会在每次请求的 response.header 会带上这个字段,下次客户端请求时将保存的 Last-Modified 作为 request.header 的 If-Modified-Since,server 比较收到的 If-Modified-Since 和资源的最后修改的时间,判断客户端缓存是否有效:

  • If-Modified-Since == 最后修改时间:缓存有效,返回 304 Not Modified,因此没有有响应主体。客户端收到该响应后,将存储的过期响应恢复为有效的,并可以在剩余的 max-aage 重复使用。
  • If-Modified-Since != 最后修改时间:缓存无效,返回 202+最新数据。

Last-Modified/If-Modified-Since 的问题

  1. 时间的最小单位是秒,如果在一秒内有更改将不能被识别。
  2. 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新发请求。
2.2、ETag/If-None-Match(HTTP/1.1)

HTTP 在 1.1 版本推出了 ETag,解决了 Last-Modified/If-Modified-Since 的问题。 客户端第一次请求时,server 生成 ETag,并作为 response.header 返回,客户端下次请求时把之前收到的 ETag 放到 request.header 的 If-Modified-Since 字段中,server 根据If-Modified-Since 和最新的 ETag 比较,如果匹配,则返回 304、没有有响应主体,否则返回 200、同时返回最新的资源。流程如下:

sequenceDiagram 客户端->>Server:第一次请求。 Server->>客户端:返回数据,ETag=XXY。 Note over 客户端:本地缓存过期 客户端->>Server:向 server 请求数据, If-None-Match=XXY Server-->>客户端:case1:ETag相同:304。 Server-->>客户端:case2:ETag不同:200、最新资源。

ETagLast-Modified 同样有优先级:ETag > Last-Modified

3、强制重新验证

某些情况下,如server资源经常变动,不让客户端使用缓存,希望始终从服务器获取最新内容,则可以使用 no-cache 指令强制验证。

通过在响应中添加 Cache-Control: no-cache 以及 Last-Modified 和 ETag,如下:

html 复制代码
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
ETag: deadbeef
Cache-Control: no-cache

<!doctype html>
...
  • 资源有更新:200 OK 响应
  • 资源无更新:304 Not Modified 响应

Cache-Control: max-age=0, must-revalidate 的组合与 no-cache 具有相同的含义。

  • max-age=0 意味着响应立即过时,而 must-revalidate 意味着一旦过时就不得在没有重新验证的情况下重用它------因此,结合起来,语义似乎与 no-cache 相同。
  • 然而,max-age=0 的使用是解决 HTTP/1.1 之前的许多实现无法处理 no-cache 这一指令------因此为了解决这个限制,max-age=0 被用作解决方法。

4、不使用缓存

no-cache 指令不会阻止响应的存储,而是阻止在没有重新验证的情况下使用本地缓存。如果不希望将数据存储在任何缓存中,请使用 no-store

html 复制代码
Cache-Control: no-store

5、Reference

相关推荐
云草桑10 分钟前
逆向工程 反编译 C# net core
前端·c#·反编译·逆向工程
布丁椰奶冻16 分钟前
解决使用nvm管理node版本时提示npm下载失败的问题
前端·npm·node.js
程序员-珍18 分钟前
SpringBoot v2.6.13 整合 swagger
java·spring boot·后端
海里真的有鱼26 分钟前
好文推荐-架构
后端
AntDreamer29 分钟前
在实际开发中,如何根据项目需求调整 RecyclerView 的缓存策略?
android·java·缓存·面试·性能优化·kotlin
Leyla41 分钟前
【代码重构】好的重构与坏的重构
前端
影子落人间1 小时前
已解决npm ERR! request to https://registry.npm.taobao.org/@vant%2farea-data failed
前端·npm·node.js
骆晨学长1 小时前
基于springboot的智慧社区微信小程序
java·数据库·spring boot·后端·微信小程序·小程序
AskHarries1 小时前
利用反射实现动态代理
java·后端·reflect
世俗ˊ1 小时前
CSS入门笔记
前端·css·笔记