浅谈浏览器的缓存机制

Posted wangziqiang123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈浏览器的缓存机制相关的知识,希望对你有一定的参考价值。

浏览器的缓存可分为HTTP缓存和离线缓存,下面将分别介绍

HTTP缓存

只有GET请求能被缓存,POST不能被缓存。
Modified Time/ETag/Expires/Cache都是HTTP协议的缓存策略

先来一个例子

当我们第二次访问百度首页,在Chrome的Network面板中打开一个静态文件时会发现响应的status是:200 OK (from disk cache),不是应该返回304 Not Modified吗?如果你知道答案,那就可以忽略本文了。
技术图片

Cache-Control

简介

w3.org 的定义是:“The Cache-Control general-header field is used to specify directives which MUST be obeyed by all caching mechanisms along the request/response chain.”

这是一个通用首部字段(就是请求报文和响应报文都能用上的字段),用来控制HTTP缓存的行为。
例如:
Cache-Control: max-age=3600, public
public意味着这个响应可以被任何人缓存
max-age则表明了该缓存有效的秒数,允许你的网站被缓存将大大减少下载时间和带宽,同时也提高浏览器的载入速度。
也可以通过设置no-cache指令来禁止缓存:
Cache-Control: no-cache

详细信息

Cache-Control也是一个通用首部字段,这意味着它能分别在请求报文和响应报文中使用。在RFC中规范了Cache-Control 的格式为:
Cache-Control: cache-directive
作为请求首部时,cache-directive 的可选值有:
技术图片

当请求头中有:Cache-Control: max-age=0,表示缓存需要进行验证(ETag||Last-Modified),如果缓存未过期,则可以使用。

当请求头中有:Cache-Control: no-cache,表示浏览器只能获取最新的文件。 和Response Header中的no-store相对应。

作为响应首部时,cache-directive 的可选值有:
技术图片

public: 共有缓存,可被缓存代理服务器缓存,比如CDN

private: 私有缓存,不能被共有缓存代理服务器缓存,可被用户的代理缓存如浏览器。

max-age=[秒]:表示在这个时间范围内缓存是新鲜的无需更新。类似Expires时间,不过这个时间是相对的,而不是绝对的。也就是某次请求成功后多少秒内缓存是新鲜的。

s-maxage=[秒]:类似max-age, 除了仅应用于共享缓存(如代理)。

no-cache:这里不是不缓存的意思,只是每次在使用缓存之前都强制发送请求给源服务器进行验证,检查文件该没改变(其实这里和ETag/Last区别不大)

no-store:就是禁止缓存,不让浏览器保留缓存副本

must-revalidate:告诉浏览器,你这必须再次验证检查信息是否过期, 返回的代号就不是200而是304了。

proxy-revalidate:类似must-revalidate,除了只能应用于代理缓存。

no-cache等同于max-age=0

Cache-Control 允许自由组合可选值,例如:
Cache-Control: max-age=3600, must-revalidate
这意味着该资源是从原服务器上取得的,且其缓存(新鲜度)的有效时间为一小时,在后续一小时内,用户重新访问该资源时无须发送请求。

缓存校验字段

Cache-Control字段能让客户端决定是否向服务器发送请求,比如设置的缓存时间未过期,那么自然直接从本地缓存取数据即可(在chrome下表现为200 from cache),若缓存时间过期了或资源不该直接走缓存,则会发请求到服务器去。

我们现在要说的问题是,如果客户端向服务器发了请求,那么是否意味着一定要读取回该资源的整个实体内容呢?

我们试着这么想——客户端上某个资源保存的缓存时间过期了,但这时候其实服务器并没有更新过这个资源,如果这个资源数据量很大,客户端要求服务器再把这个东西重新发一遍过来,是否非常浪费带宽和时间呢?

答案是肯定的,那么是否有办法让服务器知道客户端现在存有的缓存文件,其实跟自己所有的文件是一致的,然后直接告诉客户端说这东西你直接用缓存里的就可以了,我这边没更新过呢,就不再传一次过去了

为了让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,HTTP定义了以下两种校验方法。

基于修改时间

请求首部字段

  • If-Modified-Since: 比较资源最后更新的时间是否一致

响应首部字段

  • Last-Modified: 资源的最后一次修改时间

服务器将资源传递给客户端时,会将资源最后更改的时间以Last-Modified: GMT的形式加在实体首部上一起返回给客户端。

客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码即可。

基于校验码

请求首部字段

  • If-None-Match: 比较ETag是否不一致

响应首部字段

  • ETag:资源的匹配信息。

服务器会通过某种算法,给资源计算得出一个唯一标志符(比如md5标志),在把资源响应给客户端的时候,会在实体首部加上ETag: 唯一标识符一起返回给客户端。

客户端会保留该 ETag 字段,并在下一次请求时将其一并带过去给服务器。服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。

如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。

为什么要用Etag

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

组合的缓存策略

技术图片

缓存实践

使用 Expires 来兼容旧的浏览器,使用Cache-Control来更精准地利用缓存,分类管理:html, JS, CSS, Photo, Fonts,然后开启 ETag 跟 Last-Modified 功能(在较新的 nginx 上默认是同时开启了这两个功能的)进一步复用缓存减少流量。腾讯首页是60s的缓存。而静态资源则会设置一个较长的时间。
无法获取静态资源:在文件名或参数带上一串md5或时间标记符。

关于开头的例子

现在回过头来看文章开头的问题,可能会觉得答案很容易回答出来。

百度首页的资源在刷新后实际没有发送任何请求,因为 Cache-Control 定义的缓存时间段还没到期。在Chrome中即使没发送请求,但只要从本地的缓存中取,都会在Network面板显示一条状态为200且注明from cache的伪请求,其Response内容只是上一次留下的数据。

浏览器的强制策略

当下大多数浏览器在点击刷新按钮或按F5时会强行给请求加上Cache-Control:max-age=0请求字段,所以这里提及的刷新指的是选中url地址栏并按回车键(这样不会被强行加上Cache-Control)

用serviceWorker来控制HTTP缓存

在这里简单介绍一下serviceWorker,其和webWorker一样都是一种运行在浏览器中的后台线程,只不过它的权限更大,可以拦截HTTP请求,通过编程的方式去控制HTTP缓存(HTTP缓存在JS中是全局对象caches,其类型为cacheStorage),所以只有https站点才可以使用service worker,当然localhost是一个特例。
另外serviceWorker完全遵循fetch的API。

离线缓存

离线缓存的应用场景是当APP没有网络时,也可以正常进行相关操作,在JS中是全局对象applicationCache

请尽量不要使用该特性,其已经从Web标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请改用Service Workers代替。但Service Workers属于实验性质的技术,caniuse 上面显示chrome49+ firefox58+,IE系列全部不支持,属于不兼容的新技术,先保持观望。

参考资料

浅谈浏览器http的缓存机制
Service Worker初体验
MDN Service Worker
Service Worker最佳实践
JavaScript中的cacheStorage使用详解
HTTP Get,Post请求详解

原文:大专栏  浅谈浏览器的缓存机制


以上是关于浅谈浏览器的缓存机制的主要内容,如果未能解决你的问题,请参考以下文章

浅谈浏览器http的缓存机制

浅谈浏览器http的缓存机制

HTTP的缓存机制

浅谈Hibernate缓存机制:一级缓存二级缓存

Hibernate缓存机制浅谈

浅谈java两个数的异或交换以及反射和Integer的缓存机制