浏览器静态资源的缓存机制(http强缓存 协商缓存)

浏览器缓存的都是派生资源。

Webkit资源分成两类,

一类是主资源,比如HTML页面,或者下载项,

一类是派生资源,比如HTML页面中内嵌的图片或者脚本、样式表链接,

资源加载失败的处理,主资源下载失败会有报错提示,而派生资源如图片下载失败,往往只显示一个占位符。

WebKit派生资源包含的类型主要如下:

Javascript脚本(CachedScript);

CSS样式文本(CachedCSSStyleSheet);

图片(CachedImage);

字体(CachedFont);

XSL样式表(CachedXSLStyleSheet);

可以说除了主资源剩下的网络资源都是派生资源。

客户端缓存就是指用户设备中的本地资源。

不同操作系统不同浏览器缓存文件的地址也不尽相同。

本人用的mac,实在是没搞懂这个地址在哪里。

就以别人的旧图,windows chrome为例来查看下浏览器缓存文件的地址。

1)首先在chrome中输入:chrome://chrome-urls/,看到一堆列表,里面隐藏了许多浏览器的奥秘,有兴趣的可以自己深扒。

2)找到 chrome://cache(当然也可以直接输入这个地址)

打开一个css缓存文件链接,如下图pic-1

浏览器静态资源的缓存机制(http强缓存 协商缓存)

在图中可以看到,客户端保留了一个服务器端的response header。

header中有很多的字段,举例说几个。

Date字段表示此次缓存时服务器的时间。

expires字段表示过期时间,Thu, 28 Sep 2017 06:38:37GMT。

Cache-control: max-age 可以控制缓存时间。

Last-Modified和ETag也会出现在里面。

Cache-Control 是 Http1.1 中的标准,可以看成是 expires 的补充,使用的是相对时间的概念。

也就是说,强缓存和协商缓存用到的字段都在里面。

也可以得出结论,浏览器判定是否有缓存,就是浏览器去判断本地缓存目录下是否有该对应的请求

下面,我们根据浏览器请求流程来分析浏览器静态资源的缓存机制

1️⃣浏览器请求,判断无缓存时(如何判断缓存上文已讲明不再赘述):

浏览器静态资源的缓存机制(http强缓存 协商缓存)

在这个阶段(一般为第一次请求),客户端判断本地无缓存时,直接向服务器请求资源。

服务器一般会将Cache-control、expires 、last-modified、date、etag 等字段在response header 中返回,便于下次缓存。当然具体的场景,也是看服务器的约定规则设定。

2️⃣浏览器请求,判断有缓存时:

浏览器静态资源的缓存机制(http强缓存 协商缓存)

当客户端判断本地有缓存时,紧接着要判断当前缓存资源是否过期。

3️⃣判断当前缓存是否过期,就是判断是否命中强缓存的过程

强缓存

用户发送的请求,直接从客户端缓存中获取,不发送请求到服务器,不与服务器发生交互行为。

协商缓存

用户发送的请求,发送到服务器后,由服务器判定是否从缓存中获取资源。

两者共同点:客户端获得的数据最后都是从客户端缓存中获得。

在介绍如何判断缓存是否过期前,先简单介绍下强缓存相关的header字段

expires:这是http1.0时的规范;它的值为一个绝对时间的GMT格式的时间字符串,如Mon, 10 Jun 2015 21:31:12 GMT,如果发送请求的时间在expires之前,那么本地缓存始终有效,否则就会发送请求到服务器来获取资源

cache-control:max-age=number:这是http1.1时出现的header信息,主要是利用该字段的max-age值来进行判断

Cache-Control的属性设置:

1)max-age: 设置缓存的最大的有效时间,单位为秒(s)。max-age会覆盖掉Expires

2) s-maxage: 只用于共享缓存,比如CDN缓存(s -> share)。

与max-age 的区别是:max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖max-age 和 Expires.

3) public:响应会被缓存,并且在多用户间共享。默认是public。

4) private: 响应只作为私有的缓存,不能在用户间共享。如果要求HTTP认证,响应会自动设置为private。

5)no-cache: 指定不缓存响应,表明资源不进行缓存。

但是设置了no-cache之后并不代表浏览器不缓存,而是需要使用缓存协商,在缓存前要向服务器确认资源是否被更改。因此有的时候只设置no-cache防止缓存还是不够保险,还可以加上private指令,将过期时间设为过去的时间。

6)no-store: 绝对禁止缓存。

7)must-revalidate: 如果页面过期,则去服务器进行获取。

如下图,浏览器在请求某一资源时,会先获取该资源缓存的header信息,判断是否命中强缓存(cache-control和expires信息)。

浏览器静态资源的缓存机制(http强缓存 协商缓存)

1)查看是否有cache-control 的max-age / s-maxage , 如果有,则用服务器时间date值 + max-age/s-maxage 的秒数计算出新的过期时间,将当前时间与过期时间进行比较,判断是否过期

2)查看是否有cache-control 的max-age / s-maxage,如果没有,则用expires 作为过期时间比较

根据所讲的判断方式,按照上面的流程图,如果缓存没有过期,则返回的response状态为200 from memory cache或者200 from disk cache,从而直接从缓存获取资源,包括缓存header信息,不发送请求到服务器。

200 from memory cache

不访问服务器,直接读缓存,从内存中读取缓存。此时的数据时缓存到内存中的,当kill进程后,也就是浏览器关闭以后,数据将不存在。但是这种方式只能缓存派生资源

200 from disk cache 不访问服务器,直接读缓存,从磁盘中读取缓存,当kill进程时,数据还是存在。这种方式也只能缓存派生资源

一般浏览图片,如下流程:

访问-> 200 -> 退出浏览器

再进来-> 200(from disk cache) -> 刷新 -> 200(from memory cache)

如果缓存过期了,即没有命中强缓存,则判断是否命中协商缓存。

Last-modified: 表明请求的资源上次的修改时间。

If-Modified-Since:客户端保留的资源上次的修改时间。

Etag:资源的内容标识。(不唯一,通常为文件的md5或者一段hash值,只要保证写入和验证时的方法一致即可)

If-None-Match:客户端保留的资源内容标识。

协商缓存都是由服务器来确定缓存资源是否可用的,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问,这主要涉及到下面两组header字段,这两组搭档都是成对出现的,即第一次请求的响应头带上某个字段(Last-Modified或者Etag),则后续请求则会带上对应的请求字段(If-Modified-Since或者If-None-Match),若响应头没有Last-Modified或者Etag字段,则请求头也不会有对应的字段。

1)比对Last-Modified/If-Modified-Since:

浏览器会发送请求到服务器,请求会携带第一次或者上一次请求返回的有关缓存的header字段信息(Last-Modified/If-Modified-Since),服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化。

如果没有变化,则返回304 Not Modified,但是不会返回资源内容。response header中不会再添加Last-Modified的header,因为既然资源没有变化,那么Last-Modified也就不会改变。

304 Not Modified 访问服务器,发现数据没有更新,服务器返回此状态码。然后从缓存中读取数据。

如果有变化,就正常返回资源内容。浏览器直接从服务器加载资源时,Last-Modified的Header在重新加载的时候会被更新,下次请求时,If-Modified-Since会启用上次返回的Last-Modified值

2)比对Etag/If-None-Match:

这两个值是由服务器生成的每个资源的唯一标识字符串,只要资源有变化就这个值就会改变,其判断过程与Last-Modified/If-Modified-Since类似。

与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化。

HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

  • 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
  • 某些服务器不能精确的得到文件的最后修改时间。

这时,利用Etag能够更加准确的控制缓存,因为Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯

一标识符。

Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

4️⃣如果没有命中协商缓存,则直接从服务器获取数据,返回200

上面的搞懂了,下面就是个小总结

三级缓存原理

1️⃣先去内存看,如果有,直接加载

2️⃣如果内存没有,择取硬盘获取,如果有直接加载

3️⃣如果硬盘也没有,那么就进行网络请求

4️⃣加载到的资源缓存到硬盘和内存

用户行为

附上一张,用户行为影响浏览器的缓存行为。

浏览器静态资源的缓存机制(http强缓存 协商缓存)

作者: 呵呵闯

原文:https://my.oschina.net/wangch5453/blog/2995385

相关推荐