码迷,mamicode.com
首页 > Web开发 > 详细

生鲜速递:HTTP的缓存控制

时间:2021-02-25 11:48:27      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:后退   开头   get   ida   例子   查找   必须   属性   时间   

缓存(Cache)是计算机性能的一个重要概念。

由于链路漫长,网络时延不可控,浏览器使用 HTTP 获取资源的成本较高。所以,非常有必要把“来之不易”的数据缓存起来,下次再请求的时候尽可能地复用。

一、服务器的缓存控制(服务器负责控制,浏览器负责缓存)

我们以生鲜速递作为例子说一下。

如果我现在想要吃一个西瓜,打开冰箱发现冰箱是空的,那我要做的就是在生鲜超市上下单,生鲜超市配送到我手里,上面提示西瓜有效时间是5天,然后我吃了一部分西瓜后放到冰箱里头,等我想吃的时候再拿出来吃。

这里的生鲜超市就是指Web服务器,我就是浏览器角色,冰箱就是浏览器内存角色,整个流程翻译成HTTP就是:

1. 浏览器发起请求,查询浏览器缓存,没有就访问到web服务器

2. web服务器将带有资源有限时间的资源返给浏览器

3. 浏览器缓存资源,等待下次重用(资源有效时间是5天)

访问实验环境的 URI “/20-1”,看看具体的请求 - 应答过程。

技术图片

 

 

服务器标记资源有效期使用的头字段是“Cache-Control”,里面的值“max-age=30”就是资源的有效时间,相当于告诉浏览器,“这个页面只能缓存 30 秒,之后就算是过期,不能用。”就像西瓜一样,要给一个有效时间,因为过期后水果就坏了,资源也是一样,资源有时候回存在更新情况,如果一直都从缓存中拿旧数据,那肯定就不行了。

Cache-Control有如下几个属性:

1. max-age = 时间:表示资源生存时间是多少秒

2. no_store:表示资源不可缓存

3. no_cache:表示资源可以缓存,但是使用前要去服务器验证资源是否过期,是否还有更新版本出现

4. must-revalidate:表示资源缓存没过期就能继续用,过期后如果还想要的话就要去服务器进行验证。

现在我们还是用生鲜超市买西瓜作为例子学习:

1. max-age = 5 :5天内吃完水果

2. no_store:水果从超市云回来后立刻吃完,不能存放。

3. no_cache:水果从超市买回来后可以存放,但是吃水果前要去超市问一下是否还有更新鲜的水果,如果有的话就扔掉当前水果买最新的。

4. must-revalidate:水果从超市买回来后可以存放,没过期前可以正常使用,过期后就要询问超市是否可以使用。

二、客户端的缓存控制

你可以在 Chrome 里点几次“刷新”按钮,估计你会失望,页面上的 ID 一直在变,根本不是缓存的结果,明明说缓存 30 秒,怎么就不起作用呢?

其实不止服务器可以发“Cache-Control”头,浏览器也可以发“Cache-Control”,也就是说请求 - 应答的双方都可以用这个字段进行缓存控制,互相协商缓存的使用策略。

当你点“刷新”按钮的时候,浏览器会在请求头里加一个“Cache-Control: max-age=0”。因为 max-age 是“生存时间”,max-age=0 的意思就是“我要一个最最新鲜的西瓜”,而本地缓存里的数据至少保存了几秒钟,所以浏览器就不会使用缓存,而是向服务器发请求。服务器看到 max-age=0,也就会用一个最新生成的报文回应浏览器。

我们来看一下页面刷新请求时请求头中内容:

技术图片

 

 

 这说明浏览器刷新要求获取到最新最新内容,这时浏览器缓存数据就起不到任何作用了。那我们这时候就开始思考,浏览器缓存是不是没有任何作用呢?答案是否定的,我们试一下浏览器”前进‘和“后退”,然后看一下请求头是什么样子的:

技术图片

 

 

 这里我们惊奇的发现,这里的数据是从浏览器缓存中拿出来的,这是因为请求头中并没有Cache-Control字段,所以浏览器只能怪怪的回到浏览器缓存中查找内容,查找到了就返回数据了。

三、条件请求

浏览器用“Cache-Control”做缓存控制只能是刷新数据,不能很好地利用缓存数据,又因为缓存会失效,使用前还必须要去服务器验证是否是最新版。

浏览器可以用两个连续的请求组成“验证动作”:先是一个 HEAD,获取资源的修改时间等元信息,然后与缓存数据比较,如果没有改动就使用缓存,节省网络流量,否则就再发一个 GET 请求,获取最新的版本。

但这样的两个请求网络成本太高了,所以 HTTP 协议就定义了一系列“If”开头的“条件请求”字段,专门用来检查验证资源是否过期,把两个请求才能完成的工作合并在一个请求里做。而且,验证的责任也交给服务器,浏览器只需“坐享其成”。

条件请求一共有 5 个头字段,我们最常用的是“if-Modified-Since”和“If-None-Match”这两个。需要第一次的响应报文预先提供“Last-modified”和“ETag”,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的。

如果资源没有变,服务器就回应一个“304 Not Modified”,表示缓存依然有效,浏览器就可以更新一下有效期,然后放心大胆地使用缓存了。

技术图片

 

 

 ETag 是“实体标签”(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。

比如,一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分。

再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。

使用 ETag 就可以精确地识别资源的变动情况,让浏览器能够更有效地利用缓存。ETag 还有“强”“弱”之分。

强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。

还是拿生鲜速递做比喻最容易理解:

你打电话给超市,“我这个西瓜是 3 天前买的,还有最新的吗?”。超市看了一下库存,说:“没有啊,我这里都是 3 天前的。”于是你就知道了,再让超市送货也没用,还是吃冰箱里的西瓜吧。这就是“if-Modified-Since”和“Last-modified”。

但你还是想要最新的,就又打电话:“有不是沙瓤的西瓜吗?”,超市告诉你都是沙瓤的(Match),于是你还是只能吃冰箱里的沙瓤西瓜。这就是“If-None-Match”和“弱 ETag”。

第三次打电话,你说“有不是 8 斤的沙瓤西瓜吗?”,这回超市给了你满意的答复:“有个 10 斤的沙瓤西瓜”。于是,你就扔掉了冰箱里的存货,让超市重新送了一个新的大西瓜。这就是“If-None-Match”和“强 ETag”。

再来看看实验环境的 URI “/20-2”。它为资源增加了 ETag 字段,刷新页面时浏览器就会同时发送缓存控制头“max-age=0”和条件请求头“If-None-Match”,如果缓存有效服务器就会返回304:

技术图片

 

 

四、课后习题

1. Cache 和 Cookie 都是服务器发给客户端并存储的数据,你能比较一下两者的异同吗?

答:Cache 和 Cookie 的相同点是:都会保存到浏览器中,并可以设置过期时间。 Cookie 会随请求报文发送到服务器,而 Cache 不会,但可能会携带 if-Modified-Since(保存资源的最后修改时间)和 If-None-Match(保存资源唯一标识) 字段来验证资源是否过期。

2. 即使有“Last-modified”和“ETag”,强制刷新(Ctrl+F5)也能够从服务器获取最新数据(返回 200 而不是 304),请你在实验环境里试一下,观察请求头和响应头,解释原因。

答:发现强制刷新后请求头中 没有了 If-None-Match ,以及 Cache-Control字段也变了,变成了 Cache-Control: no-cache

至此,结束。

生鲜速递:HTTP的缓存控制

标签:后退   开头   get   ida   例子   查找   必须   属性   时间   

原文地址:https://www.cnblogs.com/yeyuting/p/14441815.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!