HTTP——缓存

2023年 10月 6日 95.5k 0

背景&思考

在客户端请求资源时如果每次都向 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

Expires: Tue, 28 Feb 2022 22:22:22 GMT

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

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

使用 Expires 的问题:

  • 时间格式难以解析,会引发一些问题。
  • 如果手动调整设备的时间,缓存策略就不准了。
  • 所以 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 秒后缓存过期。请求时缓存在有效期内则使用缓存:

    image.png

    超过有效期则会向 server 请求:

    image.png

    如果 ExpiresCache-Control: max-age 都设置了,优先级是:max-age > Expires。由于 HTTP/1.1 已被广泛使用,无需特地提供 Expires

    1.3、测试 Demo

    这里有个 Nodejs 写的 server Demo,可以使用 HTTPCacheServer 验证缓存策略。

    HTTPCacheServrDemo.png

    图片 example.png 设置了 max-age=10,10秒内刷新可以看到使用的是缓存:

    image.png

    第二张图片 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+最新数据。

    image.pngimage.png
    Last-Modified/If-Modified-Since 的问题:

  • 时间的最小单位是秒,如果在一秒内有更改将不能被识别。
  • 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新发请求。
  • 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,如下:

    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
    
    
    …
    
    • 资源有更新: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

    Cache-Control: no-store
    

    5、Reference

    • mdn-html
    • 阿里一面:HTTP 1.0 和 HTTP 1.1 有什么区别?
    • 一文讲透HTTP缓存之ETag
    • Expires、Cache-Control、Last-Modified和If-Modified—Since、Etag和If-None-Match
    • HTTP1.0、1.1、2.0协议的特性及区别
    • 10分钟彻底搞懂Http的强制缓存和协商缓存
    • 手把手教你如何实现服务器强缓存&协商缓存(nodejs实战篇)
    • nodejs 中的 Cache-Control

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论