初学浏览器缓存机制

作者 Simmin 日期 2017-02-17
初学浏览器缓存机制

一、缓存的目的

1.加快请求响应速度:通过将内容缓存在客户端或最近的服务器,从而不用每次加载页面都要从源服务器上去下载,耗时少。
2.节省网络带宽:内容从缓存中加载,减少网络请求,降低用户的带宽使用,同时也减少了有些内容重复传输带来的带宽浪费。
3.降低源服务器压力:在大量用户并发请求时,服务器压力必然很大,性能受到限制,而把一些静态资源分发到网络的多个节点上,实现一种负载均衡的效果,从而降低服务器压力。

二、缓存的分类

缓存分为服务器端缓存客户端缓存

服务器端缓存包括Nginx、Apache上的缓存,也包括CDN缓存

客户端缓存就是指浏览器缓存

三、浏览器缓存

3.1 缓存在哪里

浏览器一般会在用户的文件系统中创建一个目录,用于存放缓存文件,并给每个缓存文件打上一些必要的标记,比如过期时间等,但是不同的浏览器采用不同的方式来存储缓存。

IE浏览器

IE浏览器在用户本地设置了临时文件目录,可以在IE的缓存设置中找到对应的路径,如下图

打开文件夹,可以看到缓存的文件,如下图

从上图可以看出记录的四个时间:过期时间、上次修改时间、上次访问时间、上次检查时间

Firefox
Firefox存储缓存文件的方式与IE不同,它并不像IE那样将每个文件独立存储,而是采用二进制文件的方式来存储和管理缓存文件。

但是这种存储方式不便于我们了解每个URL缓存文件的状态细节,所以Firefox提供了一种简单的方式来帮助我们查看所有缓存内容,只需要在浏览器地址栏输入about:cache即可。

可以看到,Firefox在使用磁盘来存储缓存文件的同时,还使用了内存。它将命中率较高的缓存内容同时也装入内存中,这样浏览器在查找缓存的时候,将先在高速内存中查找,如果内存中没有需要的缓存,便前往磁盘缓存目录中继续查找。对于有些用户来说,这种从内存到磁盘的座机缓存机制带来了更快的缓存加载速度,比如用户在一个站点下浏览不同的资讯页面,而这些页面有着大量的重复内容,比如样式表、脚本、公共图片等,由于它们的命中率较高,所以浏览器会将它们装入内存。

AppCache:HTML5 应用程序缓存,使用HTML5,通过创建 cache manifest文件,实现web应用的离线使用。

从上图可以看到两个字段:Last Modified、Expires (Firefox 大概是把Last Modified写错了。。。)

chrome

chrome也是采用二进制文件的方式来存储和管理缓存文件。我们通过chrome://version查看存储路径

从而可以看到缓存的二进制文件

当然,这样可读性太差,同样地,chrome也为我们提供了简单直接的方式用于查看缓存文件的信息——chrome://cache

真的是很简单直接。。。点击链接便可以查看当前文件的信息

从上图可以看到四个字段:ETag、Last-Modified、Expires、Cache-Control

3.2 浏览器缓存分类

浏览器缓存主要有两类:缓存协商强缓存(也有称之为彻底缓存

强缓存:浏览器发起再次请求时,会先获取该资源的header信息,根据其中的expire和cache-control判断是否命中强缓存,若命中则直接从缓存中获取资源,包括缓存的header信息,此次请求不会与服务器进行通信。

缓存协商:浏览器发起再次请求时,如果没有命中强缓存,浏览器会发送请求到服务器,该请求会携带第一次请求返回的有关缓存的header字段信息(Last-Modified/IF-Modifed-Since、Etag/IF-None-Match),由服务器根据请求中的相关header信息来对比结果是否命中协商缓存。若命中,则服务器返回新的响应header信息更新缓存中的对应header信息,但是并不返回资源内容,它会告知浏览器可以直接从缓存获取;若未命中,则返回最新的资源内容。

3.2.1 强缓存

强缓存是利用http返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。

Expires

Expires 指示了内容过期的绝对时间,表示在内容过期之前不需要询问服务器,而直接使用本地缓存即可。它的格式为

Expires: Sun, 19 Feb 2017 10:00:00 GMT

Expires 是HTTP 1.0中规范,它有一个缺点——返回的到期时间是服务器端的时间,如果客户端的时间与服务器的时间相差很大(比如时钟不同步,或者跨时区),那么误差就很大,所以从HTTP 1.1版开始,使用Cache-Control:max-age = 秒 替代。

Cache-Control

Cache-Control描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断。所以相对Expires,Cache-Control的缓存管理更有效,更安全一些。

Cache-Control的一些值及含义:

  1. public:响应可被任何缓存区缓存
  2. private:对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当前用户的部分响应消息,此响应消息对于其他用户的请求无效。
  3. no-cache:请求或响应消息不能缓存,该选项并不是说可以设置“不缓存”,而是需要和服务器确认。
  4. no-store:在请求消息中发送将使得请求和响应消息都不使用缓存,完全不存下来。
  5. max-age:客户端可以接收生存期不大于指定时间(以秒为单位)的响应。
  6. min-fresh:客户端可以接收响应时间小于当前时间加上指定时间的响应。
  7. max-stale:客户端可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户端可以接收超出超时期指定值之内的响应消息。

Expires && Cache-Control

这两者在header中可以只启用一个,也可以同时启用,但Cache-Control的优先级高于Expires。

3.2.2 缓存协商

Last-Modified/If-Modified-Since

要配合Cache-Control使用。

Last-modified: 标识响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源最后的修改时间。

If-Modified-Since:当资源过期时(强缓存失效),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。

Etag/If-None-Match

要配合Cache-Control使用。

Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。
在Apache中,Etag的值,默认是对文件的索引节(INode)、大小(Size)和最后修改时间(MTime)进行Hash后得到的。

If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。

Last-Modified && Etag

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag一起使用时,服务器会优先验证ETag。

3.2.3 HTTP Meta标签控制缓存

<meta HTTP-EQUIV="Pragma" CONTENT="no-cache">

上面代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉去。这种方法只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。

3.3 浏览器请求流程

第一次请求:

再次请求

3.4 用户行为与缓存

还记得开头说的ctrl+F5 强制刷新吗?哪些参数依然有效呢?

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

参考文章:

[1] 《构建高性能的web站点》

[2] 详解web缓存 https://segmentfault.com/a/1190000006741200

[3] 应用程序缓存 API(简称“AppCache”)