WEB缓存策略
Posted 前端架构解读
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WEB缓存策略相关的知识,希望对你有一定的参考价值。
一、前言
在了解前端缓存之前,我们先了解对缓存的定义,在百度百科中,缓存是这样定义的:缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。
缓存的工作原理是当CPU要读取一个数据时,首先从CPU缓存中查找,找到就立即读取并送给CPU处理;没有找到,就从速率相对较慢的内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。正是这样的读取机制使CPU读取缓存的命中率非常高(大多数CPU可达90%左右),也就是说CPU下一次要读取的数据90%都在CPU缓存中,只有大约10%需要从内存读取。这大大节省了CPU直接读取内存的时间,也使CPU读取数据时基本无需等待。总的来说,CPU读取数据的顺序是先缓存后内存。
缓存作为前端性能优化最有效的手段之一,web缓存的优点有:减轻服务器压力 、减少数据传输,节省网络带宽和流量、缩短页面加载时间,提升用户体验。web缓存伴随着一个网页的整个生命周期,其中包含:web应用层缓存、浏览器缓存、CDN缓存、服务器缓存、数据库缓存等,而这些缓存与前端密切相关的只有浏览器缓存。
二、浏览器缓存
浏览器的缓存主要是由http的缓存策略、service worker以及浏览器内置的存储功能(cookie,Local Storage,Session Storage等)来提供。
(一)、缓存位置
解释浏览器缓存机制前,先简单说一下浏览器缓存的位置,从缓存位置上来说,分为四种:Service Worker、Memory Cache、Disk Cache、Push Cache,并且有各自的优先级,当依次查找且没有命中的时候,才会去http请求。
1、Service Worker
Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS(localhost也可以)。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。
2.Memory Cache
Memory Cache 也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验。
3.Disk Cache
Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
4.Push Cache
ush Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
状态 | 类型 | 说明 |
200 | form memory cache | 不请求网络资源,资源在内存当中 |
200 | form disk ceche | 不请求网络资源,在磁盘当中 |
200 | 资源大小数值 | 从服务器下载最新资源 |
304 | 报文大小 | 请求服务端发现资源没有更新,使用本地资源 |
介绍完缓存位置,我们来说说缓存策略,经过多年的发展,产生过很多存储机制,比较流行和广泛应用的有:http缓存、Web Storage、App Cache、service worker、IndexesDB、File System Api等。
(二)、http缓存
http缓存通常分为两种:强缓存和协商缓存,其中强缓存有expire、cache-control、program,协商缓存有last-modified、Etag。强缓存的优先级大于协商缓存,以下我们分别进行介绍:
1.强缓存
(1). Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点,Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间或本地时间不一致,可能会造成缓存失效。
(2). Cache-Control
Cache-Control 可以在请求头或者响应头中设置,并且可以组合使用多种指令:
指令 |
作用 |
public |
表示响应可以被客户端和代理服务器缓存 |
private |
表示响应只可以被客户端缓存 |
max-age=30 |
缓存30秒后就过期,需要重新请求 |
s-maxgae=30 |
覆盖max-age,作用一样,但是只在代理服务器中生效 |
no-store |
不缓存响应 |
no-cahe |
资源被缓存,但是立即失效,下次使用协商缓存去验证资源是否过期 |
max-stale=30 |
30秒内,即使缓存过期,也使用该缓存 |
min-fresh=30 |
希望在30秒内获取最新响应 |
public:所有内容都将被缓存(客户端和代理服务器都可缓存)。
private:所有内容只有客户端可以缓存。
no-cache:客户端缓存内容,是否使用缓存则需要经过协商缓存来验证决定。表示不使用 Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存。需要注意的是,no-cache这个名字有一点误导。设置了no-cache之后,并不是说浏览器就不再缓存数据,只是浏览器在使用缓存数据时,需要先确认一下数据是否还跟服务器保持一致。
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存。
max-age:max-age=xxx (xxx is numeric)表示缓存内容将在xxx秒后失效。max-age值最大不能超过一年
must-revalidate: 缓存过期前,可以直接使用缓存副本,但是缓存一旦过期,就必须向源服务器发送验证请求
s-maxage(单位为s):同max-age作用一样,只在代理服务器中生效(比如CDN缓存)。比如当s-maxage=60时,在这60秒中,即使更新了CDN的内容,浏览器也不会进行请求。max-age用于普通缓存,而s-maxage用于代理缓存。s-maxage的优先级高于max-age。如果存在s-maxage,则会覆盖掉max-age和Expires header。
max-stale:能容忍的最大过期时间。max-stale指令表示了客户端愿意接收一个已经过期了的响应。如果指定了max-stale的值,则最大容忍时间为对应的秒数。如果没有指定,那么说明浏览器愿意接收任何age的响应(age表示响应由源站生成或确认的时间与当前时间的差值)。
min-fresh:能够容忍的最小新鲜度。min-fresh表示了客户端不愿意接受新鲜度不多于当前的age加上min-fresh设定的时间之和的响应。
no-cache与must-revalidate的区别:
no-cache: 告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,一定要到源服务器进行副本有效性校验。
must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。
(3).Expires和Cache-Control两者对比
其实这两者差别不大,区别就在于 Expires 是http1.0的产物,Cache-Control是http1.1的产物,两者同时存在的话,Cache-Control优先级高于Expires;在某些不支持HTTP1.1的环境下,Expires就会发挥用处。所以Expires其实是过时的产物,现阶段它的存在只是一种兼容性的写法。
强缓存判断是否缓存的依据来自于是否超出某个时间或者某个时间段,而不关心服务器端文件是否已经更新,这可能会导致加载文件不是服务器端最新的内容,那我们如何获知服务器端内容是否已经发生了更新呢?此时我们需要用到协商缓存策略。
2.协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
协商缓存生效,返回304和Not Modified
协商缓存失效,返回200和请求结果
(1). Last-Modified和If-Modified-Since
浏览器在第一次访问资源时,服务器返回资源的同时,在response header中添加 Last-Modified的header,值是这个资源在服务器上的最后修改时间,浏览器接收后缓存文件和header;
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
浏览器下一次请求这个资源,浏览器检测到有 Last-Modified这个header,于是添加If-Modified-Since这个header,值就是Last-Modified中的值;服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200
但是 Last-Modified 存在一些弊端:
如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源
因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源
既然根据文件修改时间来决定是否缓存尚有不足,能否可以直接根据文件内容是否修改来决定缓存策略?所以在 HTTP / 1.1 出现了 ETag 和If-None-Match
(2).ETag和If-None-Match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到request header里的If-None-Match里,服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现ETag匹配不上,那么直接以常规GET 200 回包的形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。
(3).两者之间对比:
首先在精确度上,Etag要优于Last-Modified。
Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度;如果是负载均衡的服务器,各个服务器生成的Last-Modified也有可能不一致。
第二在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。
第三在优先级上,服务器校验优先考虑Etag
3.http缓存与用户行为
(三). APP Cache与Service worker
APP Cache是html5 提供一种 应用程序缓存(Cache Manifest) 机制,因为有诸多问题,标准已经决定废弃,感兴趣的小伙伴可以查找资料看看。我们着重介绍Service worker。
http cache已经足够强大了,为什么还需要Service worker,因为http cache是基于后端控制的,而Service worker是基于前端控制的。service worker出来之前,有人尝试过前端控制,就是用Local Storage或者Session Storage来存储一些数据,但这种方法缺少很多关键的浏览器基础设施,比如异步存储、静态资源存储、URL匹配、请求拦截等功能。而Service Worker的出现填补了这些基础设施缺少的问题。(Service Worker并非专门为缓存而设计,它还可以解决Web应用推送、后台长计算等问题)
最后遗留几个问题:
1、ajax请求与fetch的区别
2、浏览器是根据什么决定「from disk cache」与「from memory cache」?
3、preload与prefetch?
4、Web Socket、Web Worker和Service Worker的区别
5、浏览器的缓存已经介绍完了,那么浏览的存储你了解多少呢?
参考文献
1、深入了解浏览器的缓存机制 https://www.jianshu.com/p/54cc04190252
以上是关于WEB缓存策略的主要内容,如果未能解决你的问题,请参考以下文章