前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HTTP缓存

HTTP缓存

作者头像
多云转晴
发布2020-06-03 09:18:01
7630
发布2020-06-03 09:18:01
举报
文章被收录于专栏:webTowerwebTower

HTTP 缓存不是必须的,但重用缓存的资源通常是必要的。它可以减少服务器的压力,如果不使用缓存,每次发起请求都要求服务器发送相应数据,很多时候服务器发来的内容并没有发生变化,就会“浪费”服务器带宽。可以在客户端设置缓存,给缓存加上过期时间,如果期限没到就是用本地缓存的内容。然而常见的 HTTP 缓存只能存储 GET 响应,对于其他类型的响应则无能为力。

缓存头部

HTTP 相关的缓存头部一般有:

  • Cache-Control 通用的首部,它是缓存控制字段;
  • Expires 响应首部,代表资源过期时间;
  • Last-Modified 响应首部,表示资源的最新修改时间,由服务器告诉浏览器;
  • If-Modified-Since 请求首部,表示资源的最新修改时间,由浏览器告诉服务器。它和 Last-Modified 是一对,它俩用来做对比;
  • Etag 响应首部,用于资源标识,由服务器告诉浏览器;
  • If-None-Match 请求首部,缓存资源标识由服务器告诉服务器(就是上一次服务器给的 Etag)。它和 Etag 是一对,它俩用来做对比;

除了头部,有些状态码与缓存也有些关系:

  • 200 则表示为成功。一个包含例如 HTML 文档,图片,或者文件的响应。
  • 304 说明无需再次传输请求的内容,也就是说可以使用缓存的内容。
  • 206 不完全的响应,只返回局部的信息,常用在断点续传中。

Expires 响应首部很好理解,就是设置一个过期时间,值是一个 http 时间戳,如:

代码语言:javascript
复制
Expires: Wed, 21 Oct 2019 07:28:00 GMT

设置后,当客户端再次发送请求时就会检查 Expires 的过期时间,如果过期了就去向服务端发起请求,没过期就是用本地的缓存。如果不想使用缓存,可以将值设置成 0,即该资源已经过期。

Expires 有一个问题,假如缓存时间到了,需要重新向服务端获取数据,而服务端并没有更新内容,这就会造成“浪费”。最好需要一种比较“精确”的方式,当服务端真正更新数据时才让客户端使用新的内容,不然就让它使用缓存。

Last-ModifiedIf-Modified-Since 就是为了解决这个问题的。在客户端第一次请求某个资源时,服务器会发来一个 Last-Modified 头部,它与 Expires 头部的值很像,不过它表示的是资源做出修改的日期和时间。它可以与 Expires 头部一起使用。

当再次发起网络请求时,客户端会向服务器提供一个 If-Modified-Since 请求首部,如果之前响应带有 Expires 头部,会先检查缓存时间到了没,如果没到继续使用,过期了就请求服务器。服务器收到请求首部,拿到 If-Modified-Since 的日期,它是上一次响应首部 Last-Modified 的值,与现在文件的最新修改日期做对比,如果文件修改了,就返回修改后的文件内容和新的 Last-Modified 响应首部,状态码为 200,而如果发现文件并没有修改,就返回状态码 304,且不带有消息主体。

last-modified

last-modified 的不足

Last-Modified 响应首部的精确度不高,它只能精确到一秒。一个日访问量很大的网站,后端在某个时间修改了文件,在这个时间点可能会有很多人在访问该资源,总会有一些人收不到最新的资源(几毫秒内很多人访问,但 Last-Modified 精度却是“一秒”)。ETag 可以做到更“精确”。

浏览器与服务器在过期时间 Expires + Last-Modified 的基础上,增加一对文件内容的唯一对比标记 —— ETagIf-None-Match

如果资源更改,则一定要生成新的 Etag 值,Etag 类似于指纹。客户端再请求时,如果设定的 Expires 过期了,就会使用 If-None-Match 头将上一次响应时的 Etag 值带到后端(这个值之所以能获取到是因为浏览器把这个响应首部缓存了),后端用该值与最新的文件变动后的 ETag 值做对比,如果两个值不相同,就返回资源内容和新的 Etag 值,响应码为200;如果值相同,说明资源还没更新,就返回 304 状态码。

ETag 的行为与 Last-Modified 的行为很相似,都是做对比然后做出反馈。但 ETag 更精确,只要文件变更,唯一标识也会变更。这个唯一标识可以有多种方式生成,比如生成资源内容的散列值、最后修改时间的时间戳的哈希值或者简单的使用自己定义的版本号。

如果 If-None-MatchIf-Modified-Since 同时出现,If-None-Match 的优先级更高。

ETag 的值有强弱之分,强 ETag 值无论发生多么细微的变化都会改变其值。 弱 ETag 值比较宽松,只有资源发生了根本变化,产生差异时才会改变ETag的值。要将 ETag 值设置成弱比较需在字段值的最开始处附加 W/ 标记。如:

代码语言:javascript
复制
ETag: W/"as463c"

条件请求

形如 If-xxx 格式的请求首部字段可称之为条件请求,服务器在接收到这些条件请求时,只有判断条件为真才执行请求。除了上面用于缓存的 If-Modified-SinceIf-None-Match 两个条件请求之外,还有三个常见的条件请求:

  • If-Match 在请求方法为 GETHEAD 的情况下,它的值与 ETag 的值匹配一致时服务器才接受请求。而对于 PUT 或其他非安全方法来说,只有在满足条件的情况下才可以将资源上传。如果匹配不一致,则返回状态码 412(Precondition Failed,先决条件失败)的响应。
  • If-Range 这个请求首部的值也会与 ETag 值或更新的日期时间(Last-Modified)进行匹配,如果一致,那么就作为范围请求处理If-Range 应与 Range 请求首部一起使用。
  • If-Unmodified-Since 功能与 If-Modified-Since 相反,作用是告知服务器,在指定的日期事件之后资源如果 未发生更新,才处理请求 。如果在指定日期后发生了更新,则以状态码 412 作为相应返回。

If-Range 请求首部可以让 Range 头在满足一定条件时才起作用,而且服务器回复 206 部分内容状态码,以及 Range 首部字段请求的资源的相应部分。如果条件不满足,服务器将会返回 200 OK 状态码,并返回完整的请求资源。If-Range 头通常用于断点续传的下载过程中,如果上一次下载时中断了,这一次下载时确保资源没有发生改变(如果发生改变 ETag 或者 Last-Modified 就会变化,If-Range 与之对比发现不一致,就会重新下载,而不是接着上一次接着下载)

If-Match 请求首部通常也是搭配 Range 首部一起使用。这样可以保证新请求的范围与之前请求的范围是对同一份资源的请求,如果 ETagIf-Match 值不一致,说明不是同一份资源,或者这个资源已经被修改。If-Match 的值还可以是星号*,这表示服务器会忽略 ETag 的值,只要资源存在就处理请求。带有 If-Match 请求头时,服务器是无法使用弱ETag值的。

Expires 与 max-age

Expires 的值是“绝对”时间,哪一年哪一月都写得很清楚。服务端发来的 Expires 日期会缓存到客户端。这有一个问题,因为 Expires 用的是服务端的时间,如果客户端的时间与服务器的时间相差很大(客户端与服务端的时间不一致),就会出现很大的误差。比如服务端发去的 Expires 是四月一号,而客户端的日期已经是四月三号了,一对比就是过期的内容。

Cache-Control 有一个 max-age 指令,它是相对时间,如:

代码语言:javascript
复制
Cache-Control: max-age=604800

单位是秒,上面表示再过 604800 秒后该资源会被认为过期。因为是相对时间,即使客户端与服务端时间不一致也没关系。

如果 Expiresmax-age 同时设置,会优先处理 max-age,忽略掉 Expires 首部字段。

no-cache 与 no-store

Cache-Control 是通用的消息头部,通过指定指令来实现缓存机制,可以指定多个指令,指令以逗号分隔。有些指令前端、后端都可以去设置。no-cacheno-store 这两个指令就是“通用”的指令。

no-cache

中如果包含 no-cache 指令,表示客户端可以缓存资源,每次使用缓存资源前都必须重新验证其有效性。这意味着每次都会发起 HTTP 请求。设置 max-age=0 的功能与之类似。以客户端角度看,no-cache 表示强制向源服务器再次验证有效期,以服务端角度看,no-cache 表示资源可以缓存,但在每次使用前都要由服务端确认一下。

no-store

no-store 在客户端与服务端功能一样,表示不缓存请求或者响应的任何内容。这意味着每次请求都会发起网络请求拿到数据。对于机密或敏感的文件(如包含银行账户的 HTML 页面)最好使用这个指令。

must-revalidate

这个指令通常与 max-age 一起使用,当设定的 max-age 到期后,客户端会向服务端发起网络请求,验证缓存资源是否还有效。它像是延迟版的 no-cache

代码语言:javascript
复制
Cache-Control: max-age:600, must-revalidate

max-stale 与 min-fresh

这两个指令都有值,单位是秒,而且都是请求指令才拥有。

max-stale 表明客户端愿意接收一个已经过期的资源,即使已经过期也照常使用。如果不指定参数值,过期之后就会发起请求,接受响应,而如果设置了参数值,在指定的时间内,缓存仍会被接受。

min-fresh 表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应。例如:

代码语言:javascript
复制
Cache-Control: min-fresh=100

在 100 秒内,资源的有效期限到了,这资源就无法作为响应返回。

Pragma

它是 HTTP/1.0 的通用头,它用来向后兼容只支持 HTTP/1.0 协议的缓存服务器。它有一个 no-cache 指令,效果与 Cache-Control 中的 no-cache 一致。

总结

缓存的处理过程可以简单地分为几步:

  1. 首先在缓存中搜索指定资源的副本,如果命中就执行第二步;
  2. 对资源副本进行新鲜度检测(If-None-Match),检测文档是否过期,如果不新鲜就执行第三步;
  3. 客户端与服务器进行再验证,验证通过(即没有过期)就更新资源副本的新鲜度,再返回这个资源副本(此时的响应码为 304 Not Modified);
  4. 如果服务端验证不通过,就从服务器返回资源,再将最新资源的副本放入缓存中;
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebTower 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 缓存头部
    • last-modified 的不足
      • 条件请求
        • Expires 与 max-age
        • no-cache 与 no-store
          • no-cache
            • no-store
              • must-revalidate
                • max-stale 与 min-fresh
                • Pragma
                • 总结
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档