http缓存机制
一句话概况:本地缓存请求到的资源,后续请求尽可能直接复用这些资源,减少Http请求,从而显著提高网站和应用程序的性能。
那么什么时候缓存资源到本地?缓存资源什么时候过期?什么情况下使用这些缓存的资源呢?本文就带着这三个问题开始展开。
HTTP缓存机制流程

因为一般只有
GET
请求才会被缓存,所以这里泛指一般的GET
资源请求。
强缓存
不需要额外向服务端发送请求,直接使用本地缓存。在Chrome浏览器中本地强缓存分为两类,一类是disk cache
,一类是memory cache
,查看devtools中的Networks会看到请求状态为200
,并且后面跟着from disk cache
和from memory cache
的请求就是使用了强缓存,如下面两个图。


本人也尚未了解Chrome浏览器如何控制两种强缓存,故不展开了,以免误导读者,希望能有高手指出!!!!这里放上找到的Chrome官方文档中的描述,其大体意思是两种强缓存策略与渲染进程的生命周期有关,渲染进程的周期又大致与tab选项卡相对应:
Chrome employs two caches — an on-disk cache and a very fast in-memory cache. The lifetime of an in-memory cache is attached to the lifetime of a render process, which roughly corresponds to a tab. Requests that are answered from the in-memory cache are invisible to the web request API.
是否使用强缓存由HTTP的三个头部字段来控制:Expires
、Pragma
、Cache-Control
。
Expires
Exipres
字段是Http/1.0
中的字段,其优先级在三个缓存控制字段中最低。

如图所示,响应头中Expires
的值是一个时间戳,发起请求时,如果本地系统时间在这个时间戳之前,则缓存有效,否则缓存失效,进入协商缓存。若该响应头中Expires
设置为无效的日期,比如 0
, 则代表着过去的日期,即该资源已经过期。
Cache-Control

Cache-Control
是 HTTP/1.1
中规定的通用头部字段,常用属性如下:
no-store
:禁止使用缓存,每次请求都去服务端拿最新的资源;no-cache
:不使用强缓存,直接进入协商缓存模块,向服务端请求校验资源是否“新鲜”;private
:私有缓存,中间代理服务端不可缓存资源public
:公共缓存,中间代理服务端可以缓存资源max-age
:单位:秒,缓存的最长有效时间。其起始时间为缓存时响应头中的Date字段,即有效期到responseDate + max-age,发起请求时超过该时间则缓存过期。must-revalidate
:缓存一旦过期,则必须重新向服务端验证。
Pragma
Pragma
是 HTTP/1.0
中规定的通用头部字段,用于向后兼容只支持 HTTP/1.0
协议的缓存服务端。这个字段只有一个值:no-cache
,其表现行为与Cache-Control: no-cache
一致,但是HTTP
的响应头没有明确定义这个属性,所以它不能拿来完全替代HTTP/1.1
中定义的Cache-control
头。
如果
Pragma
和Cache-Control
两个字段同时存在,Pragma
的优先级大于Cache-Control
。
协商缓存
当强缓存过期或者请求头字段设置不走强缓存,比如Cache-Control:no-cache
和Pragma:no-cache
,则进入协商缓存部分。协商缓存涉及两对头部字段,分别是Last-Modified
/If-Modified-Since
、和ETag
/If-None-Match
。
若请求头中携带If-Modified-Since
或If-None-Match
字段,则会发起去服务端校验资源是否有变化,如果有变化,则未命中缓存,服务端返回200
,浏览器计算响应体资源是否缓存并使用资源;如果未变换,则命中缓存,返回304
,浏览器根据响应头更新缓存头部信息,延长有效期,并直接使用缓存。

Last-Modified/If-Modified-Since
Last-Modified
/If-Modified-Since
的值是资源修改时间。第一次请求资源时,服务端将资源的最后修改时间放到响应头的 Last-Modified
字段中,第二次请求该资源时,浏览器会自动将该资源上一次响应头中的Last-Modified
的值放到第二次请求头的If-Modified-Since
字段中,服务端比较服务端资源的最后一次修改时间和请求头中的If-Modified-Since
的值,如果相等,则命中缓存返回 304
,否则,返回200。
ETag/If-None-Match
ETag
/If-None-Match
的值是一串hash
值(hash
算法不统一),是资源的标识符,当资源内容发生变化,其hash
值也会改变。其过程与上面的相似,不过服务端是比较服务端资源的hash
值和请求头中的If-None-Match
的值,但比较方式有所区别,因为ETag
有两种类型:
强校验
:资源hash值具有唯一性,一旦变化则hash也变化。弱校验
:资源hash值以W/
开头,若资源变化较小,则同样可能命中缓存。
例如下面这样:
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" ETag: W/"0815"
两者区别
ETag
/If-None-Match
优先级比Last-Modified
/If-Modified-Since
高;Last-Modified
/If-Modified-Since
有个1S问题,即服务端在1S内修改文件,且再次受到请求时,会错误的返回304
。
代理服务缓存

Vary
是HTTP/1.1
中的一个头字字段,其值为请求头中的字段,如上图中的Accept-Encoding
,可以是多个,以逗号分割,其记录了代理服务器返回资源参考了哪些请求头字段。代理服务器拿到源服务器的响应报文,会根据 Vary
里的字段列表,缓存不同版本的资源。当有资源请求再次访问时,代理服务器会分析请求头字段,返回正确的版本。
总结
在实际应用过程,对一些更新不频繁的资源合理使用缓存机制,可以有效提高系统的响应速度,提高用户体验。