浏览器缓存机制详解参考

前言

  对于浏览器缓存,相信很多开发者对它真的是又爱又恨。一方面极大地提升了用户体验,而另一方面有时会因为读取了缓存而展示了“错误”的东西,而在开发过程中千方百计地想把缓存禁掉。那么浏览器缓存究竟是个什么样的神奇玩意呢?

     缓存种类:CDN缓存、数据库缓存、代理服务器缓存和浏览器缓存

什么是浏览器缓存:

  简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。至于浏览器和网站服务器是如何标识网站页面是否更新的机制,将在后面介绍。

  上图就是使用了缓存的栗子,在页面请求之后,web资源都被缓存了,在后面的重复请求中,可以看到许多资源都是直接从缓存中读取的(from cache),而不是重新去向服务器请求。

 

为什么使用缓存:

(1)减少网络带宽消耗

  无论对于网站运营者或者用户,带宽都代表着金钱,过多的带宽消耗,只会便宜了网络运营商。当Web缓存副本被使用时,只会产生极小的网络流量,可以有效的降低运营成本。

(2)降低服务器压力

  给网络资源设定有效期之后,用户可以重复使用本地的缓存,减少对源服务器的请求,间接降低服务器的压力。同时,搜索引擎的爬虫机器人也能根据过期机制降低爬取的频率,也能有效降低服务器的压力。

(3)减少网络延迟,加快页面打开速度

  带宽对于个人网站运营者来说是十分重要,而对于大型的互联网公司来说,可能有时因为钱多而真的不在乎。那Web缓存还有作用吗?答案是肯定的,对于最终用户,缓存的使用能够明显加快页面打开速度,达到更好的体验。

 

浏览器端的缓存规则:

  对于浏览器端的缓存来讲,这些规则是在HTTP协议头和HTML页面的Meta标签中定义的。他们分别从新鲜度校验值两个维度来规定浏览器是否可以直接使用缓存中的副本,还是需要去源服务器获取更新的版本。

  新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下条件,浏览器会认为它是有效的,足够新的:

    1. 含有完整的过期时间控制头信息(HTTP协议报头),并且仍在有效期内;

    2. 浏览器已经使用过这个缓存副本,并且在一个会话中已经检查过新鲜度;

  满足以上两个情况的一种,浏览器会直接从缓存中获取副本并渲染。

  校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag),它可以用来作为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过期,浏览器需求重新获取资源内容。

 

浏览器缓存的控制:

  (1)使用HTML Meta 标签

  Web开发者可以在HTML页面的<head>节点中加入<meta>标签,代码如下

<meta http-equiv="Pragma" content="no-cache">  
<!- Pragma是http1.0版本中给客户端设定缓存方式之一,具体作用会在后面详细介绍 -->

  上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。但是!这里有个坑...

  事实上这种禁用缓存的形式用处很有限:

    a. 仅有IE才能识别这段meta标签含义,其它主流浏览器仅识别“Cache-Control: no-store”的meta标签。

    b. 在IE中识别到该meta标签含义,并不一定会在请求字段加上Pragma,但的确会让当前页面每次都发新请求(仅限页面,页面上的资源则不受影响)

  (2)使用缓存有关的HTTP消息报头

  在这里就需要先跟大家介绍一下HTTP的相关知识。一个URI的完整HTTP协议交互过程是由HTTP请求和HTTP响应组成的。有关HTTP详细内容可参考《Hypertext Transfer Protocol — HTTP/1.1》、《HTTP协议详解》等。

  在HTTP请求和响应的消息报头中,常见的与缓存有关的消息报头有:

规则消息包头值/示例类型作用
新鲜度Pragmano-cache响应告诉浏览器忽略资源的缓存副本,每次访问都需要去服务器拉取【http1.0中存在的字段,在http1.1已被抛弃,使用Cache-Control替代,但为了做http协议的向下兼容,很多网站依旧会带上这个字段】
 Expires Mon, 15 Aug 2016 03:56:47 GMT响应启用缓存和定义缓存时间。告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求【http1.0中存在的字段,该字段所定义的缓存时间是相对服务器上的时间而言的,如果客户端上的时间跟服务器上的时间不一致(特别是用户修改了自己电脑的系统时间),那缓存时间可能就没啥意义了。在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代】
  Cache-Controlno-cache 响应告诉浏览器忽略资源的缓存副本,强制每次请求直接发送给服务器,拉取资源,但不是“不缓存”
  no-store响应 强制缓存在任何情况下都不要保留任何副本
   max-age=[秒]响应指明缓存副本的有效时长,从请求时间开始到过期时间之间的秒数
   public响应任何路径的缓存者(本地缓存、代理服务器),可以无条件的缓存改资源
   private响应只针对单个用户或者实体(不同用户、窗口)缓存资源
  Last-ModifiedMon, 15 Aug 2016 03:56:47 GMT响应告诉浏览器这个资源最后的修改时间。服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端【只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
  If-Modified-SinceMon, 15 Aug 2016 03:56:47 GMT 请求其值为上次响应头的Last-Modified值,再次向web服务器请求时带上头If-Modified-Since。web服务器收到请求后发现有头If-Modified-Since则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),包括更新Last-Modified的值,HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304(无需包体,节省浏览),告知浏览器继续使用所保存的cache
校验值ETag"fd56273325a2114818df4f29a628226d" 响应告诉浏览器当前资源在服务器的唯一标识符(生成规则又服务器决定)
 If-None-Match "fd56273325a2114818df4f29a628226d"请求当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match(Etag的值)。web服务器收到请求后发现有头If-None-Match则与被请求资源的相应校验串进行比对,决定返回200或304

   在我们对HTTP请求头和响应头的部分字段有了一定的认识之后,我们接下来就来讨论不同字段之间的关系和区别:

  · Cache-Control与Expires

  Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires

  · Last-Modified/ETag与Cache-Control/Expires

  配置Last-Modified/ETag的情况下,浏览器再次访问统一URI的资源,还是会发送请求到服务器询问文件是否已经修改,如果没有,服务器会只发送一个304回给浏览器,告诉浏览器直接从自己本地的缓存取数据;如果修改过那就整个数据重新发给浏览器;

  Cache-Control/Expires则不同,如果检测到本地的缓存还是有效的时间范围内,浏览器直接使用本地副本,不会发送任何请求。两者一起使用时,Cache-Control/Expires的优先级要高于Last-Modified/ETag。即当本地副本根据Cache-Control/Expires发现还在有效期内时,则不会再次发送请求去服务器询问修改时间(Last-Modified)或实体标识(Etag)了。

  一般情况下,使用Cache-Control/Expires会配合Last-Modified/ETag一起使用,因为即使服务器设置缓存时间, 当用户点击“刷新”按钮时,浏览器会忽略缓存继续向服务器发送请求,这时Last-Modified/ETag将能够很好利用304,从而减少响应开销。

   · Last-Modified与ETag

你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

  1. Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的新鲜度
  2. 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存
  3. 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。Etag的服务器生成规则和强弱Etag的相关内容可以参考,《互动百科-Etag》和《HTTP Header definition》,这里不再深入。

  注意:

  1. Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存,但是需要注意的是分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败,Yahoo建议分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样,因为除了 last-modified、inode 也很难保持一致)。

   2. Last-Modified/If-Modified-Since要配合Cache-Control使用,Etag/If-None-Match也要配合Cache-Control使用。

 

浏览器HTTP请求流程:

  第一次请求:

  

  再次请求:

  

 

用户行为与缓存:

   浏览器缓存行为还有用户的行为有关,具体情况如下:

用户操作Expires/Cache-ControlLast-Modified/Etag
地址栏回车有效有效
页面链接跳转有效有效
新开窗口有效有效
前进、后退有效有效
F5刷新无效(BR重置max-age=0)有效
Ctrl+F5刷新无效(重置Cache-Control=no-cache)无效(请求头丢弃该选项

 

不能缓存的请求:

  当然并不是所有请求都能被缓存,无法被浏览器缓存的请求如下:

    1. HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告诉浏览器不用缓存的请求

    2. 需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的

    3. 经过HTTPS安全加密的请求(有人也经过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public之后,能够对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)

    4. POST请求无法被缓存

    5. HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存

 

感觉还是朦胧的?换一种说法吧:

浏览器的缓存问题,主要指的是http的缓存——即协议层。而h5新增的storage和数据库缓存,那是应用层缓存,并不被计入本篇的分析内容里面。下面我们正式开始来进行缓存的分析。

协议层的缓存,其实,可以被分成强制缓存和对比缓存。

强制缓存

首先,我们先来看一张强制缓存时的时序图,来了解一下强制缓存在不同情况下的请求模式:

force cache

从图中,我们不难看出,只有当缓存失效时,才会去服务器获取最新资源的方式,就是强制缓存。而在协议层的字段中,可以造成强制缓存的字段有两个Expires和Cache-Control。

1.0的时候见到我——Expires

最早使用的是Expires字段,该字段表示缓存到期时间,即有效时间+当时服务器的时间,然后将这个时间设置在header中返回给服务器。因此,该时间是一个绝对时间,举例说明:

Expires: Thu, 10 Nov 2017 08:45:11 GMT

图片示例:

![expires](
http://lhbzimo.oss-cn-shenzhe...

在响应消息头中,设置这个字段之后,就可以告诉浏览器,在未过期之前不需要再次请求。

但是,这个字段设置时有缺点

由于是绝对时间,用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效,重新请求该资源,同时,还导致客户端与服务端的时间不一致,致使缓存失效。

1.1的时候我来了——Cache-Control

已知Expires的缺点之后,在HTTP/1.1中,增加了一个字段Cache-Control,该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求

这两者的区别就是前者是绝对时间,而后者是相对时间。我们不妨举个例子来说明一下:

Cache-Control: max-age=2592000

图片示例:

Cache-Control

下面列举一下Cache-Control的字段可以带的值:

  1. max-age:即最大有效时间,在上面的例子中我们可以看到
  2. no-cache:表示没有缓存,即告诉浏览器该资源并没有设置缓存
  3. s-maxage:同max-age,但是仅用于共享缓存,如CDN缓存
  4. public:多用户共享缓存,默认设置
  5. private:不能够多用户共享,HTTP认证之后,字段会自动转换成private。

总结一下,自从http1.1开始,Expires逐渐被Cache-Control取代。Cache-Control是一个相对时间,即使客户端时间发生改变,相对时间也不会随之改变,这样可以保持服务器和客户端的时间一致性。而且Cache-Control的可配置性比较强大。

对比缓存

扯完强制缓存,我们来看看对比缓存。在解释这个之前,是否可以先猜想一下,强制缓存是,缓存在未过有效期时,不需要请求资源。那么,对比缓存的原理又该如何呢?

废话不多说,我们也先从对比缓存的时序图讲起,如图:

compare cache

对比缓存的过程是,先从缓存中获取对应的数据标识,然后向服务器发送请求,确认数据是否更新,如果更新,则返回新数据和新缓存;反之,则返回304状态码,告知客户端缓存未更新,可继续使用。

这正好弥补了一些强制缓存的缺陷。对比缓存主要应用于一些时常需要动态更新的资源文件。

对比缓存在协议里的字段是Last-Modified和If-Modified-Since。

别人的好伙伴——Last-Modified

Last-Modified:服务器告知客户端,资源最后一次被修改的时间,例如

Last-Modified: Thu, 10 Nov 2015 08:45:11 GMT

last-modified

If-Modified-Since:再次请求时,请求头中带有该字段,服务器会将If-Modified-Since的值与Last-Modified字段进行对比,如果相等,则表示未修改,响应304;反之,则表示修改了,响应200状态码,返回数据。

这个字段可以和Cache-Control配合使用。

但是他还是有一定缺陷的:

  1. 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
  2. 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。

我来完善它——Etag

由于Last-modified还是存在缺陷的,尽管大多数情况下,会使用它,但当遇到我们上面所说的场景时,我们可能就需要了解一下,我们另一个小伙伴了——Etag。

Etag存储的是文件的特殊标识(一般都是hash生成的),服务器存储着文件的Etag字段,可以在与每次客户端传送If-no-match的字段进行比较,如果相等,则表示未修改,响应304;反之,则表示已修改,响应200状态码,返回数据。

最后,通过一张原理图,我们来加深一下记忆:

etag

至此为止,两种缓存类型的缓存方式已经阐述完成了,不知你是否已经心中已经有个大致的印象,当别人问起时,你可以对答如流。希望我们一同进步吧,fighting。

浏览器行为引起的不同

最后,我们来聊聊浏览器行为会引起缓存的变化吧。

下面说一下浏览器的行为会产生怎样的请求:

  1. 刷新网页 => 如果缓存没有失效,浏览器会直接使用缓存;反之,则向服务器请求数据
  2. 手动刷新(F5) => 浏览器会认为缓存失效,在请求服务器时加上Cache-Control: max-age=0字段,然后询问服务器数据是否更新。
  3. 强制刷新(Ctrl + F5) => 浏览器会直接忽略缓存,在请求服务器时加上Cache-Control: no-cache字段,然后重新向服务器拉取文件。

移动端的缓存处理

在PC端或许这样子的缓存机制就已经足够了,因为PC端不需要为网络的问题担心。

但是,移动端却不行,任何一个网络请求的增加,对于移动端的加载消耗时间都是比较大的(谁叫移动端的网太差呢,3G、2G)。那么,上述的缓存有什么问题呢?其实,强制缓存是没有太大问题的,因为只要缓存不到期,是不会想服务器发送请求的;但是如果是对比缓存的情况下,304的问题就比较巨大,因为它会造成无用的请求。每次在使用缓存前,都会向服务器发送请求确认,导致网络的延时。

一次完美的缓存必须保证两点:

  1. 数据缓存之后,尽量减少服务器的请求
  2. 如果资源更新的话,必须使得客户端的资源一起更新。

所以,一般我们会运用的方式是:

在资源文件后面加上表示,如config.f1ec3.js、config.v1.js之类的,然后给资源设置较长的缓存时间,如一年

Cache-Control: max-age=31536000

这样子,就不会造成304的回包现象。
然后一旦资源发生更新时,我们可以改变资源后面的标识符,实现静态资源非覆盖式更新。

总结

本篇大致分析了浏览器缓存部分的分类情况,以及细化分析。主要可分为:

  1. 强制缓存

    • Expires字段
    • Cache-Control字段
  2. 对比缓存

    • Last-Modefied字段
    • Etag标识
  3. 浏览器行为引起的缓存变化
  4. 移动端的缓存策略

其他参考资料:

1. http://www.cnblogs.com/520yang/articles/4807408.html  浏览器 HTTP 协议缓存机制详解

2. https://my.oschina.net/leejun2005/blog/369148  浏览器 HTTP 协议缓存机制详解

3. http://web.jobbole.com/82997/  浏览器缓存机制浅析

4. http://www.alloyteam.com/2012/03/web-cache-2-browser-cache/  Web浏览器的缓存机制 

5. http://www.cnblogs.com/vajoy/p/5341664.html  浅谈浏览器http的缓存机制

6. http://mp.weixin.qq.com/s/yf0pWRFM7v9Ru3D9_JhGPQ  浏览器缓存机制剖析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值