图解 HTTP 的缓存机制 _ 实用 HTTP

Cache-Control 中设定的 max-age 很好理解,就是设定缓存超时的时间,HTTP 缓存是限定一个超时的秒数,来确定缓存失效的时间。

上古时期还会使用 expires 来决定超时的日期,但是已经被废弃了,如果和 Cache-Control 同时存在,以 Cache-Control 为准。

在此时间间隔范围内,客户端不会再向服务端发送新的请求。当资源距离上一次缓存的时间间隔,大于 120 秒后,客户端才会再次向服务端发送请求。

假如没有数据令牌的情况下,大概步骤应该是这样的:

1. 客户端会首先找到本地缓存,然后发现它已经失效,无法再次使用。

2. 客户端再次向服务端发出新的请求,并获取完整的数据再次进行缓存。之后再刷新该缓存的超时时间。

但是这是一件效率非常低的事情,服务端并无法确定所持有的源资源什么时候会失效,所以提供的 max-age 值,只是一个参考值,是需要取平衡的,太短会导致请求频繁,太长又会导致无法及时刷新客户端资源。而此时再次请求的时候,是存在一定的概率,客户端缓存的数据和服务端上持有的数据是一致的,我们就不需要再次对此数据资源进行二次缓存,直接使用客户端之前缓存的数据即可,同时还需要刷新缓存超时时间。

这正是数据验证令牌(ETag)想要解决的问题,服务端生成并返回的这个数据指纹令牌,通常就是返回数据的 Hash 值或者其他数据指纹,客户端无需关心它的生成规则,只需要知道它是当前数据的一个唯一标识。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

客户端需要在下次请求时将其通过 If-None-Match 这个请求报文头,将此验证令牌发送至服务端,如果数据令牌指纹和服务端当前的数据一致,则标识资源未发生新的变化。就会返回一个 304 的状态码,表示可以继续使用客户端本地缓存的数据,并刷新超时时间。注意当响应码为 304 的时候,它是不包含数据内容的。

通常此缓存操作对我们都是透明的,它是浏览器和开源网络库的基本实现,我们无需自己去判断 max-age 和 ETag 的值,这一步我们只需要确定服务端对此有支持即可。

这里只是提到了 If-None-Match,它标识比较 ETag 是否不一致,除此之外,还有一些其他的相关报文头,例如 If-Match,有兴趣可以查阅相关资料。

2.5 Cache-Control

前面举的例子中,我们只为 Cache-Control 设定了一个 max-age,但是其实还有一些更丰富的配置。

从缓存性能最优化的角度来看,最佳的缓存是无需与服务端通信的缓存,可以通过缓存来消灭网络延迟以及数据请求,从而来提高用户的体验。

Cache-Control 是在 HTTP/1.1 中被定义的,它可以用于取代之前的缓存策略,现在所有的浏览器都支持 Cache-Control ,它已经成为一种通用的标准。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Cache-Control 还有一些更灵活的配置,用来对缓存做一些更细致的操作。

1. “no-cache” 和 “no-store”

这两个参数都表示每一次请求,都需要真实的发送一个网络请求。

它们之间的区别在于,“no-cache”并不是真的不缓存数据,它只是要求每次都确认资源是否过期,也就是它会利用数据令牌 ETag 来一定程度的减小传输的流量。

而 “no-store” 完全是要求客户端,每次都重新请求数据并下载最新的数据,不做任何缓存处理。这种不缓存的策略,也包括中间连接的代理、网关 等中间传输的通道,也一并不对数据进行缓存,每次都从源服务器上获取数据。

2. “public” 和 “private”

“public” 是一种默认的策略,表示当前缓存是开放的,任何请求响应的中间环节,都可以对其进行缓存,如果我们不显式指定,则当前为 “public” 缓存。

与之相对的 “private”,则表示当前响应是针对单个用户的,并非通用数据,因此不建议任何中间缓存对其进行缓存。例如:浏览器就是一个比较私人的缓存源,它会缓存 “private” 的缓存,而 CDN 则不会。

三、最佳的缓存策略树

前面提到,缓存的核心目的就是为了快,能让下次使用的时候快速复用。所以在理想情况下,我们应该将响应数据尽可能多的缓存,尽可能的缓存足够长的时间,并且为每个资源提供单独的数据验证令牌,以便在时间过期之后快速校验。

但是任何事情都是要取其平衡点的,不存在什么最佳缓存策略,并非所有响应资源都需要加缓存,这就需要根据业务场景来设定。

这里给出一个增加 HTTP 缓存的通用策略树,你在对响应增加缓存的时候,可以参考它来执行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正常情况下,我们针对不同的响应属性,会对它设置不同的缓存策略,下面根据场景,举几个例子。

3.1 用户相关的数据

和单个用户紧密相关的数据,通常我们是不建议使用缓存的,但是依然存在几个等级。

1. 严格不使用缓存

Cache-Control:no-store

2. 允许客户终端缓存,但是每次使用都需要确认

Cache-Control:no-cache
ETag:“cxmyDev1234”

3. 允许客户终端短时间缓存

Cache-Control:private max-age=600
ETag:“cxmyDev1234”

3.2 通用数据

一些通用响应资源,更新的频率非常的低,我们可以根据需要调整 max-age 的大小即可。

Cache-Control:max-age=86400
ETag:“cxmyDev1234”

四、废弃和更新缓存的响应

缓存的策略,一旦确定并下发到客户端,服务端就失去了对齐的控制权。也就是说,如果我们设定了 max-age,在此资源有效期超时之前,哪怕服务端的源资源已经被替换修改,我们也没有一个合适的时机去通知客户端更新新的响应数据。

那么有没有什么好的策略去标记资源废弃?同时又能友好的利用缓存策略。

在互联网上,所有服务上的资源,都有一个对应的 URL(统一资源定位符),它可以明确说明如何从一个精确且固定的位置获取资源。而 HTTP 缓存,也是依赖于 URL 的,注意 URL 是大小写敏感的,同一个 URL 表示同一个请求响应,依此来判断缓存和后续缓存的复用。

所以我们是可以在 URL 上做文章的。

4.1 浏览器的废弃策略

前面提到,浏览器是天然支持 HTTP 缓存的,对于浏览器来说,它所面对的就是一个个 HTML 页面,页面内会包含一些 CSS、Image、JavaScript、JSON 资源和数据。

针对不同的资源和数据,我们可以在其 URL 上,增加数据令牌指纹,当资源变动的时候,同时也去刷新改指纹令牌。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

到这里就很好理解了:

  • HTML 页面,使用 no-cache,强制每次都向源服务器确认数据。
  • CSS文件通常变动的频率非常低,所以可以允许中间层缓存,并且缓存时间为一年不过期。
  • JavaScript内有业务逻辑,可以设定为只允许客户终端缓存。
  • getUserInfo,是为个人用户数据相关,这里推荐可缓存,但是需要每次向服务器重新确认。

4.2 App 接口的缓存策略

在 App 中使用的接口,其实和网页又不一样,HTML 网页的结构类似一个树形结构,先通过获取 .html 文件获取其内所有资源的表,然后依次根据缓存策略进行访问。

但是在 App 中,和服务器的交互都是通过数据接口来实现的,就不存在最开始获取一个类似 HTML 文件这样的树形接口,每个接口都是一个个“孤岛”,可以单独存在。我们就无法提前知道某个接口的响应数据已经过期,同时也无法修改 URL 上携带的数据指纹令牌。

但是其实我们是可以通过 App 和设备的一些固有信息,作为 URL 的参数传递,以此来刷新数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例如这里 /app/main 获取主页的数据,这里将当前 App 的版本号当参数拼接在 URL 的后面,以此方式来强制不同的版本,刷新不同的数据。避免刚升级上来的 App,还在使用旧版本的数据。

这个例子中,版本号只是其中一个维度,如果有必要,还可以传递其他维度的信息,例如当前网络状态,当前用户 id 等等。

五、小结

到这里我们基本上把 HTTP 的缓存所有相关的内容都讲了一遍,这里简单总结一下。

  • HTTP 缓存依赖 URL 做唯一标识,不同的 URL 使用不同的缓存。
  • Cache-Control 可以控制缓存策略,共有或者私有、缓存超时时长等。
  • 通过 ETag 来标记数据指纹令牌,以此来确定响应数据是否更新。
  • 应该为每个响应资源提供对应的缓存策略。
  • 如果需要废弃之前的缓存,可以利用修改请求 URL 的方式,将数据指纹令牌追加在 URL 之后,以此来更新数据。

关于 HTTP 缓存,你还有什么更好的想法,可以在留言区讨论。

参考资料:


公众号后台回复成长『成长』,将会得到我准备的学习资料,也能回复『加群』,一起学习进步;你还能回复『提问』,向我发起提问。
推荐阅读:

先自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以扫码领取!!!!

最后

愿你有一天,真爱自己,善待自己。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可免费领取!

c=“https://i-blog.csdnimg.cn/blog_migrate/56314db9452462b7db44b7f998212254.jpeg” />

最后

愿你有一天,真爱自己,善待自己。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可免费领取!

  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值