Open fengshi123 opened 5 years ago
缓存的重要性不言而喻,通过网络请求资源缓慢并且降低了客户端的用户体验,增添了服务端的负担。很多短期之内不会经常发生变化的资源文件没必要每次访问都想服务端进行数据请求,而缓存策略的使用就是为了改善客户端的呈现时间,降低服务端的负担。 以下“理论知识 + 实践操作”来彻底弄懂 HTTP 缓存机制及原理!
如果喜欢或者有所启发,欢迎 Star~,对作者也是一种鼓励。
为方便大家理解,我假设览器存在一个缓存数据库,用于存储缓存信息。在客户端第一次请求数据时,此时缓存数据库中没有对应的缓存数据,需要请求服务器,服务器返回后,将数据存储至缓存数据库中。如下流程图所示:
根据是否需要重新向服务器发起请求来分类,将HTTP缓存规则分为两大类(强制缓存,对比缓存)在详细介绍这两种规则之前,先通过时序图的方式,让大家对这两种规则有个简单了解。
(1)已存在缓存数据时,仅基于强制缓存,请求数据的流程如下所示:
(2)已存在缓存数据时,仅基于对比缓存,请求数据的流程如下所示:
我们可以看到两类缓存规则的不同,强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。
两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。
注意:
(1)如果使用了Pragma: 'no-cache'的话,再设置Expires或者Cache-Control,就没有用了,说明Pragma的权值比后两者高。
Pragma: 'no-cache'
Expires
Cache-Control
Pragma
(2)如果设置了Expires之后,客户端在需要请求数据的时候,首先会对比当前系统时间和这个Expires时间,如果没有超过Expires时间,则直接读取本地磁盘中的缓存数据,不发送请求。
2.1、Cache-Control 字段
2.1.1、Cache-Control 作为请求头字段
(1)Cache-Control: no-cache
使用no-cache指令的目的是为了防止从缓存中返回过期的资源。 客户端发送的请求中如果包含 no-cache 指令,则表示客户端将不会接收缓存的资源。每次请求都是从服务器获取资源,返回304。
no-cache
(2)Cache-Control: no-store
使用no-store 指令表示请求的资源不会被缓存,下次任何其它请求获取该资源,还是会从服务器获取,返回 200,即资源本身。
no-store
2.1.2、Cache-Control 作为响应头字段
Cache-Control: public
当指定使用 public指令时,则明确表明其他用户也可利用缓存。
public
Cache-Control: private
当指定 private 指令后,响应只以特定的用户作为对象,这与 public 指令的行为相反。 缓存服务器会对该特定用户提供资源缓存的服务,对于其他用户发送 过来的请求,代理服务器则不会返回缓存。
private
Cache-Control: no-cache
如果服务器返回的响应中包含no-cache指令,每次客户端请求,必需先向服务器确认其有效性,如果资源没有更改,则返回304.
Cache-Control: no-store
不对响应的资源进行缓存,即用户下次请求还是返回 200,返回资源本身。
Cache-Control: max-age=604800(单位:秒)
资源缓存在本地浏览器的时间,如果超过该时间,则重新向服务器获取。
2.2、请求头部字段 & 响应头部字段
2.2.1、请求头部字段
2.2.2、响应头部字段
(1)If-None-Match的优先级比If-Modified-Since高,所以两者同时存在时,遵从前者。
If-None-Match
If-Modified-Since
1、实验1 — 请求的资源没修改,验证2种缓存出现的情形
服务端使用node.js , 客户端使用 axios进行请求:
node.js
axios
1.1、请求头部 / 响应头部 设置
(1)服务端响应头部设置:
res.setHeader('Cache-Control', 'public, max-age=10');
(2)客户端请求头部使用默认设置
1.2、实验步骤
(1)请求 3 次,第一次请求请求资源;第二次在10秒内再次请求该资源,第三次在 10 秒后再次请求该资源(实验过程中,服务端的资源没有进行改变)
1.3、实验结果
3 次请求的 HTTP 信息如下图所示,从图中的信息可以得出,第一次请求该资源是从服务器获取;第二次(10 秒内)请求该资源是直接从浏览器缓存中获取该资源(没有向服务器确认);第三次(10 秒后)请求该资源时,因为资源缓存时间(10 秒)过期,所以向服务器获取资源,服务器判断该资源与本地缓存的资源没有做更改,所以返回 304,让客户端直接从浏览器缓存中获取该资源;以下,根据 HTTP 头部信息详细介绍三个操作的交互过程。
1.3.1、第一次请求资源
第一次请求资源的请求头部和响应头部的截图如下所示,因为第一次请求该资源,本地并没有缓存,所以直接从服务器获取该资源;我们从截图可以看到,服务器返回改资源的响应头部中包含3个属性与资源缓存相关:
(1)cache-control: public, max-age=10
cache-control: public, max-age=10
缓存规则的设置,我们这个示例中,设置缓存规则为 public, 并且设置缓存过期时间为10秒;
(2)etag: W/"95f15b-16994d7ebf6"
etag: W/"95f15b-16994d7ebf6"
资源的唯一标识符,客户端下次访问该资源时,会在请求头中携带 etag 去向服务器确认,该资源是否被修改;
etag
(3)last-modified: Tue, 19 Mar 2019 07:26:12 GMT
)last-modified: Tue, 19 Mar 2019 07:26:12 GMT
资源最后一次修改时间,客户端下次访问该资源时,会在请求头中携带该信息去向服务器进行匹配,该资源是否被修改;
1.3.2、第二次请求资源(10秒内,即在缓存时间失效前)
第二次请求资源的请求头部和响应头部的截图如下所示,因为第二次请求该资源,该资源本地缓存还没失效,所以就直接从浏览器缓存中获取该资源。
1.3.3、第三次请求资源(10秒后,即在缓存时间失效后)
第三次请求资源的请求头部和响应头部的截图如下所示,因为第三次请求该资源,该资源本地缓存已经失效,所以在请求头部中加入If-Modified-Since和 If-None-Match属性,来向服务器进行确认该资源是否有被更改。
2、实验2 — 请求的资源进行修改,验证2种缓存出现的情形
服务端使用node.js, 客户端使用 axios进行请求:
2.1、请求头部 / 响应头部 设置
res.setHeader('Cache-Control', 'public, max-age=20');
2.2、实验步骤
(1)请求 3 次,第一次请求资源;然后在服务器对请求的资源进行修改,第二次在 20 秒内再次请求该资源,第三次在 20 秒后再次请求该资源
2.3、实验结果
3 次请求的 HTTP 信息如下图所示,从图中的信息可以得出,第一次请求该资源是从服务器获取;第二次(20 秒内)请求该资源是直接从浏览器缓存中获取该资源(没有向服务器确认);第三次(20 秒后)请求该资源时,因为资源缓存时间(20 秒)过期,所以向服务器获取资源,服务器判断该资源与本地缓存的资源不同,所以重新返回该资源;以下,根据 HTTP 头部信息详细介绍三个操作的交互过程。
2.3.1、第一次请求资源
第一次请求资源的请求头部和响应头部的截图如下所示,具体详细信息与 1.3.1 小节相同,在此不同重复介绍。
2.3.2、第二次请求资源(20 秒内,即在缓存时间失效前)
第二次请求资源的请求头部和响应头部的截图如下所示,(注意:即使此时服务器上的资源已经更改,但是由于缓存在浏览器中的资源没有过期,所以还是从缓存中返回旧资源)。
2.3.3、第三次请求资源(20 秒后,即在缓存时间失效后)
第三次请求资源的请求头部和响应头部的截图如下所示,因为第三次请求该资源,该资源本地缓存已经失效,所以在请求头部中加入If-Modified-Since和If-None-Match属性,来向服务器进行确认该资源是否有被更改,从下图中可以看到,响应头部的属性 etag与 请求头部的属性If-None-Match 不同,响应头部的属性If-Modified-Since与 请求头部的属性 last-modified不同;所以服务器返回该资源的最新资源。
last-modified
1、对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
2、对于比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
总结流程图如下所示:
写的很好,只是有个地方感觉没有写清楚,强制缓存与比较缓存是通过配置确定的,还是在max-age内就是强制缓存,max-age外就执行比较缓存?
图片挂了, 麻烦作者改下链接~
已经更改图片链接~
一、前言
缓存的重要性不言而喻,通过网络请求资源缓慢并且降低了客户端的用户体验,增添了服务端的负担。很多短期之内不会经常发生变化的资源文件没必要每次访问都想服务端进行数据请求,而缓存策略的使用就是为了改善客户端的呈现时间,降低服务端的负担。 以下“理论知识 + 实践操作”来彻底弄懂 HTTP 缓存机制及原理!
如果喜欢或者有所启发,欢迎 Star~,对作者也是一种鼓励。
二、缓存规则及解析
为方便大家理解,我假设览器存在一个缓存数据库,用于存储缓存信息。在客户端第一次请求数据时,此时缓存数据库中没有对应的缓存数据,需要请求服务器,服务器返回后,将数据存储至缓存数据库中。如下流程图所示:
根据是否需要重新向服务器发起请求来分类,将HTTP缓存规则分为两大类(强制缓存,对比缓存)在详细介绍这两种规则之前,先通过时序图的方式,让大家对这两种规则有个简单了解。
(1)已存在缓存数据时,仅基于强制缓存,请求数据的流程如下所示:
(2)已存在缓存数据时,仅基于对比缓存,请求数据的流程如下所示:
我们可以看到两类缓存规则的不同,强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。
两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。
三、缓存常用字段
1、http1.0时期的缓存方案
注意:
(1)如果使用了
Pragma: 'no-cache'
的话,再设置Expires
或者Cache-Control
,就没有用了,说明Pragma
的权值比后两者高。(2)如果设置了
Expires
之后,客户端在需要请求数据的时候,首先会对比当前系统时间和这个Expires
时间,如果没有超过Expires
时间,则直接读取本地磁盘中的缓存数据,不发送请求。2、http1.1 时期的缓存方案
2.1、Cache-Control 字段
2.1.1、Cache-Control 作为请求头字段
(1)Cache-Control: no-cache
使用
no-cache
指令的目的是为了防止从缓存中返回过期的资源。 客户端发送的请求中如果包含no-cache
指令,则表示客户端将不会接收缓存的资源。每次请求都是从服务器获取资源,返回304。(2)Cache-Control: no-store
使用
no-store
指令表示请求的资源不会被缓存,下次任何其它请求获取该资源,还是会从服务器获取,返回 200,即资源本身。2.1.2、Cache-Control 作为响应头字段
Cache-Control: public
当指定使用
public
指令时,则明确表明其他用户也可利用缓存。Cache-Control: private
当指定
private
指令后,响应只以特定的用户作为对象,这与public
指令的行为相反。 缓存服务器会对该特定用户提供资源缓存的服务,对于其他用户发送 过来的请求,代理服务器则不会返回缓存。Cache-Control: no-cache
如果服务器返回的响应中包含
no-cache
指令,每次客户端请求,必需先向服务器确认其有效性,如果资源没有更改,则返回304.Cache-Control: no-store
不对响应的资源进行缓存,即用户下次请求还是返回 200,返回资源本身。
Cache-Control: max-age=604800(单位:秒)
资源缓存在本地浏览器的时间,如果超过该时间,则重新向服务器获取。
2.2、请求头部字段 & 响应头部字段
2.2.1、请求头部字段
2.2.2、响应头部字段
注意:
(1)
If-None-Match
的优先级比If-Modified-Since
高,所以两者同时存在时,遵从前者。四、实验验证
1、实验1 — 请求的资源没修改,验证2种缓存出现的情形
服务端使用
node.js
, 客户端使用axios
进行请求:1.1、请求头部 / 响应头部 设置
(1)服务端响应头部设置:
res.setHeader('Cache-Control', 'public, max-age=10');
(2)客户端请求头部使用默认设置
1.2、实验步骤
(1)请求 3 次,第一次请求请求资源;第二次在10秒内再次请求该资源,第三次在 10 秒后再次请求该资源(实验过程中,服务端的资源没有进行改变)
1.3、实验结果
3 次请求的 HTTP 信息如下图所示,从图中的信息可以得出,第一次请求该资源是从服务器获取;第二次(10 秒内)请求该资源是直接从浏览器缓存中获取该资源(没有向服务器确认);第三次(10 秒后)请求该资源时,因为资源缓存时间(10 秒)过期,所以向服务器获取资源,服务器判断该资源与本地缓存的资源没有做更改,所以返回 304,让客户端直接从浏览器缓存中获取该资源;以下,根据 HTTP 头部信息详细介绍三个操作的交互过程。
1.3.1、第一次请求资源
第一次请求资源的请求头部和响应头部的截图如下所示,因为第一次请求该资源,本地并没有缓存,所以直接从服务器获取该资源;我们从截图可以看到,服务器返回改资源的响应头部中包含3个属性与资源缓存相关:
(1)
cache-control: public, max-age=10
缓存规则的设置,我们这个示例中,设置缓存规则为
public
, 并且设置缓存过期时间为10秒;(2)
etag: W/"95f15b-16994d7ebf6"
资源的唯一标识符,客户端下次访问该资源时,会在请求头中携带
etag
去向服务器确认,该资源是否被修改;(3
)last-modified: Tue, 19 Mar 2019 07:26:12 GMT
资源最后一次修改时间,客户端下次访问该资源时,会在请求头中携带该信息去向服务器进行匹配,该资源是否被修改;
1.3.2、第二次请求资源(10秒内,即在缓存时间失效前)
第二次请求资源的请求头部和响应头部的截图如下所示,因为第二次请求该资源,该资源本地缓存还没失效,所以就直接从浏览器缓存中获取该资源。
1.3.3、第三次请求资源(10秒后,即在缓存时间失效后)
第三次请求资源的请求头部和响应头部的截图如下所示,因为第三次请求该资源,该资源本地缓存已经失效,所以在请求头部中加入
If-Modified-Since
和If-None-Match
属性,来向服务器进行确认该资源是否有被更改。2、实验2 — 请求的资源进行修改,验证2种缓存出现的情形
服务端使用
node.js
, 客户端使用axios
进行请求:2.1、请求头部 / 响应头部 设置
(1)服务端响应头部设置:
res.setHeader('Cache-Control', 'public, max-age=20');
(2)客户端请求头部使用默认设置
2.2、实验步骤
(1)请求 3 次,第一次请求资源;然后在服务器对请求的资源进行修改,第二次在 20 秒内再次请求该资源,第三次在 20 秒后再次请求该资源
2.3、实验结果
3 次请求的 HTTP 信息如下图所示,从图中的信息可以得出,第一次请求该资源是从服务器获取;第二次(20 秒内)请求该资源是直接从浏览器缓存中获取该资源(没有向服务器确认);第三次(20 秒后)请求该资源时,因为资源缓存时间(20 秒)过期,所以向服务器获取资源,服务器判断该资源与本地缓存的资源不同,所以重新返回该资源;以下,根据 HTTP 头部信息详细介绍三个操作的交互过程。
2.3.1、第一次请求资源
第一次请求资源的请求头部和响应头部的截图如下所示,具体详细信息与 1.3.1 小节相同,在此不同重复介绍。
2.3.2、第二次请求资源(20 秒内,即在缓存时间失效前)
第二次请求资源的请求头部和响应头部的截图如下所示,(注意:即使此时服务器上的资源已经更改,但是由于缓存在浏览器中的资源没有过期,所以还是从缓存中返回旧资源)。
2.3.3、第三次请求资源(20 秒后,即在缓存时间失效后)
第三次请求资源的请求头部和响应头部的截图如下所示,因为第三次请求该资源,该资源本地缓存已经失效,所以在请求头部中加入
If-Modified-Since
和If-None-Match
属性,来向服务器进行确认该资源是否有被更改,从下图中可以看到,响应头部的属性etag
与 请求头部的属性If-None-Match
不同,响应头部的属性If-Modified-Since
与 请求头部的属性last-modified
不同;所以服务器返回该资源的最新资源。五、总结
1、对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
2、对于比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
总结流程图如下所示:
如果喜欢或者有所启发,欢迎 Star~,对作者也是一种鼓励。