谈谈 HTTP 缓存


这几天在面试的时候被问到了 HTTP 是如何进行缓存的,我在网上查了查,简单整理了下笔记。

1. Pragma:

在 http 1.0 时代,给客户端设定缓存方式可通过两个字段 Pragma 和 Expires,虽然这两个字段早可抛弃,但为了做 http 协议的向下兼容,你还是可以看到很多网站依然带上这两个字段。

在响应报文中当该字段值为 "no-cache" 的时候,会通知客户端不要对该资源进行缓存,每次都得向服务器发一次请求才行。

2. Expires:

有了 Pragma 来禁用缓存,那如果需要设置缓存的话就得有个东西来设置缓存的时间,对于 http 1.0 来说,Expires 就是做这件事的。

Expires 的值对应一个 GMT (格林尼治时间),比如 “Mon, 22 Mar 2017 11:12:01 GMT” 来告诉浏览器资源缓存过期时间,如果还没有超过该时间点则不发请求,直接返回 200 OK。(from cache)

=所以如果面试官问你 http 响应 200 OK (from cache) 是否有发送 http 请求,那么就请回答没有,它们都是直接读取缓存资源的。

当两者一起使用时候,Pragma 优先级更高,即当 Pragma 设置禁用缓存时,又给 Expires 定义一个还未到期的时间,会发现仍然会发送新的请求,所以 Pragma 优先级比 Expires 高。

当然现在不提倡这样的方式,因为它有两个致命的缺点:

  1. Expires 定义的缓存时间是相对于服务器上的时间而言的,而浏览器在判断的时候是基于客户端的系统时间的,如果用户修改了自己电脑的系统时间,那么这个缓存时间将没有任何意义。
  2. 假如客户端上某个资源缓存时间过期了,但此时其实服务器并没有更新过该资源,那么这时候客户端要求服务器重新把东西再发送过来一遍,会浪费带宽和时间,这显然是不合理的,我们需要有一种正确的机制用来判断东西到底可以直接使用缓存。

3. Cache-Control:

针对上述 Expires 时间是相对于服务器而言,无法保证和客户端时间统一的问题,http 1.1 新增加了 Cache-Control 来定义缓存过期时间,若报文中同时出现 Pragma、Expires 和 Cache-Control,那么会以 Cache-Control 为准。

它可以由多个可选值组合而成,比如:

Cache-Control: max-age=3600, must-revalidate

它意味着该资源从原服务器上取得的,且其缓存的有效时间为一小时,在后续一小时内,用户重新访问该资源则无需发送新的请求。

Cache-Control: no-cache, no-store

它告诉浏览器不使用缓存,要求每次请求要向服务器发送请求,并且所有内容都不会被保存到缓存或者临时文件中。

当然组合方式也有一定的限制,比如 no-cache 不能和 max-age 一起搭配使用。

4. Last-Modified:

为了解决前面遗留下的第二个问题,来保证当客户端上某个资源保存的缓存时间过期了,但这时候其实服务器并没有更新过这个资源,服务器能够正确处理这样的请求,而不是重新发送资源。

为了让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,http 1.1 新增了几个首部字段来做这件事情。

服务器将资源传递给客户端时,会将资源最后更改的时间以 "Last-Modified: GMT" 的形式加入到响应报头上一起返回给客户端。

客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中发送,若传递的值与服务器上该资源的最终修改时间一致,则说明该资源没有被修改过。

请求报文有两个字段用来传递该标记时间,分别是:

  • If-Modified-Since: Last-Modified-value

该字段告诉服务器如果客户端传来的最后修改时间和服务器上的一致,直接返回 304 状态码即可,当前各大浏览器均是使用该字段来向服务器传递保存的 Last-Modified 的值。

  • If-Unmodified-Since: Last-Modified-value

该字段告诉服务器如果客户端传来的最后修改时间和服务器上的不一致,则直接返回 412 状态码(Precondition Failed)。

这两者有什么区别呢:

If-Modified-Since:

从字面上看,就是说:如果从某个时间点算起,如果文件被修改了。

  1. 如果真的被修改:那么就开始传输,服务器返回:200 OK
  2. 如果没有被修改:那么就无需传输,服务器返回:304 Not Modified.

If-Unmodified-Since:

从字面上看, 意思是: 如果从某个时间点算起, 文件没有被修改。

  1. 如果没有被修改:则开始继续传送文件,服务器返回: 200 OK
  2. 如果文件被修改:则不传输,服务器返回:412 Precondition failed (预处理错误)

比如断点续传就会使用到这个字段(一般会指定 Range 参数),要想断点续传,那么文件就一定不能被修改,否则就不是同一个文件。

当然,Last-Modified 也有一定的缺陷,因为如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为 Last-Modified 时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)

5. Etag:

为了解决上述 Last-Modified 可能存在的不准确的问题,http 1.1 还推出了 Etag 实体字段。

服务器会通过某种算法,给资源计算得出一个唯一标志符(比如 md5 标志),在把资源响应给客户端的时候,会在响应报文中加上 "Etag: 唯一标识符" 返回给客户端。

客户端会保存该 Etag 字段,并在下次请求的时候将其作为请求头某字段的值发送请求,服务器只需要比较客户端传来的 Etag 跟自己服务器上该资源的 Etag 是否一致,就可以很好地判断资源相对客户端而言是否被修改过了。

那么客户端如何把标记在资源上的 Etag 发送给服务器呢?

  • If-None-Match: ETag-value
If-None-Match: "qwerrgerg-1332"

告诉服务端如果 ETag 没匹配上需要重发资源数据,否则直接回送304 和响应报头即可。

当前各浏览器均是使用的该请求首部来向服务器传递保存的 ETag 值。

  • If-Match: ETag-value

该字段告诉服务器如果客户端传来的 Etag 值跟服务器上的不一致,则直接返回 412 状态码(Precondition Failed)。

这两者跟 Last-modified 十分类似。

如果 Last-Modified 和 ETag 同时被使用,则要求它们的验证都必须通过才会返回304,若其中某个验证没通过,则服务器会按常规返回资源实体及200状态码。

总结:

当我们在一个项目上做 http 缓存的应用时,我们还是会把上述提及的大多数首部字段均使用上,例如使用 Expires 来兼容旧的浏览器,使用 Cache-Control 来更精准地利用缓存,然后开启 ETag 跟 Last-Modified 功能进一步复用缓存减少流量。

转载:https://zhuanlan.zhihu.com/p/25647421

个人建了前端学习群,旨在一起学习前端。纯净、纯粹技术讨论,非前端人员勿扰!入群加我微信iamaixiaoxiao。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值