【浏览器】缓存机制

252 阅读21分钟

引言

WEB 缓存知识体系: image.png

浏览器从输入URL到界面展示的流程: image.png

一、浏览器缓存

浏览器缓存是为了节约网络的资源加载浏览,浏览器在用户磁盘上对最近请求过的文档(html、css、js、image等静态资源)进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。

注意点: HTTP缓存是浏览器缓存的核心。一般意义来讲,浏览器缓存是指HTTP缓存。严格意义来讲,浏览器缓存 = HTTP缓存 + 数据缓存。

1.1 缓存位置

强缓存我们会把资源放到 memory cache 和 disk cache中。

存储图像和网页等资源主要缓存在disk cache,操作系统缓存文件等资源大部分都会缓存在memory cache中。具体操作浏览器自动分配,看谁的资源利用率不高就分给谁。

查找浏览器缓存时会按顺序查找: Service Worker-->Memory Cache-->Disk Cache-->Push Cache。

1.1.1 Service Worker

是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 service worker 的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。

Service Worker 的缓存与浏览器其它内建的缓存机制不同,它可以让我们自由控制缓存哪些文件?如何匹配缓存?如何读取缓存,并且缓存是持续性的

1.1.2 memory cache

MemoryCache,内存中的缓存。就是将资源缓存到内存中,等待下次访问时不需要重新下载资源,而直接从内存中获取,读取速度快且高效。但它是短暂的,当浏览器或Tab页面关闭,内存就会被释放。

Webkit早已支持memoryCache。 目前Webkit资源分成两类,一类是主资源,比如HTML页面,或者下载项,一类是派生资源,比如HTML页面中内嵌的图片或者脚本链接,分别对应代码中两个类:MainResourceLoader和SubresourceLoader。虽然Webkit支持memoryCache,但是也只是针对派生资源,它对应的类为CachedResource,用于保存原始数据(比如CSS,JS等),以及解码过的图片数据。

1.1.3 disk cache

DiskCache顾名思义,就是将资源缓存到磁盘中,等待下次访问时不需要重新下载资源,而直接从磁盘中获取,它的直接操作对象为CurlCacheManager。

  • Memory Cache 和 Disk Cache

    • 相同点

      • 只能存储一些派生类资源文件
    • 不同点

      • Memory Cache 退出进程时数据会被清除
      • Disk Cache退出进程时数据不太会被清除
    • 存储资源

      • Memory Cache 一般脚本、字体、图片会存在内存当中
      • Disk Cache 一般非脚本会存在内存当中,如CSS

    因为CSS文件加载一次就可渲染出来,我们不会频繁读取它,所以它不适合缓存到内存中,但是js之类的脚本却随时可能会执行,如果脚本在磁盘当中,我们在执行脚本的时候需要从磁盘取到内存中来,这样IO开销就很大了,有可能导致浏览器失去响应。

  • prefetch cache(预期缓存)

    prefect cache 是预加载的一种方式,被标记为 prefect 的资源,将会被浏览器在空闲时间加载

    link 标签上带了 prefect,再次加载会出现

1.1.4 Push Cache

Push Cache(推送缓存)是HTTP2.0 中的内容,当以上三种缓存都没有命中,它才会被使用。它只在会话(session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。

三级缓存原理 (访问缓存优先级)

  • 先在内存中查找,如果有,直接加载。
  • 如果内存中不存在,则在硬盘中查找,如果有直接加载。
  • 如果硬盘中也没有,那么就进行网络请求。
  • 请求获取的资源缓存到硬盘和内存。

二、数据缓存

数据缓存方式:cookie、localStorage、sessionStorage、IndexedDB、Web SQL

2.1 cookie

  • 优点:访问方便,使用方便,且还有多个属性可以设置

  • 缺点:

    • 大小受限,只有4KB左右;
    • 明文传递,存在安全风险;
    • 客户端还可以禁用;不能跨域
  • 设置方式:通过JS设置或服务器的response header设置

  • 应用场景:接口请求中的身份认证

  • 自动删除场景:

    • 会话cookie会在游览器关闭时删除
    • 持久cookie会在到达失效日期时删除
    • 游览器中的cookie数量达到限制时会删除旧的cookie为新建cookie创建空间

2.2 localStorage 和 sessionStorage

  • localStoragesessionStorage是为了解决cookie的存储空间不足的问题

  • 相同点

    • HTML5新增的特性
    • 存储大小一般均为5M左右。localStorage实际上是主要跟各厂家游览器有关,是在2.5~10MB之间
    • 访问策略:都有同源策略限制,跨域无法访问
    • 存储位置:数据仅在客户端中保进行存储,并不参与和服务器的通信(不会随着 http 请求发送到服务器)
    • 存储形式:以 key 和 value 的形式进行存储数据, value 值必须为字符串,不为字符串会自动转型( value 如果是对象则需要转为 json 进行存储)
  • 不同点

    • 存储类型:

      • localStorage:本地存储
      • sessionStorage:会话存储
    • 生命周期:

      • localStorage:存储的数据是永久性的。除非人为清除,
      • sessionStorage:随游览器窗口/标签页关闭而删除,或人为清除。
    • 作用域:

      • localStorage:在同一个浏览器内,同源文档之间共享 localStorage 数据,可以互相读取、覆盖、清除(同浏览器限制、同源限制)

      • sessionStorage:有同一浏览器、同一窗口的同源文档才能共享数据(同浏览器限制、同源限制、同标签页限制),或包含多个iframe标签页且他们属于同源页面

  • 新增/修改(它们的方式都是有一样的,就以sessionStorage为例)

    • 通过 setItem 添加、修改数据

        sessionStorage.setItem('name', 'qianyin');
        sessionStorage.setItem('name', 'linheng');
        sessionStorage.setItem('user1', {name: 'qianyin'});
        sessionStorage.setItem('user2', JSON.stringify({name: 'qianyin'}));
      
    • 通过对象的形式添加、修改数据

        sessionStorage.name = 'qianyin';
        sessionStorage.name = 'linheng';
        sessionStorage.user1 = {name: 'qianyin'};
        sessionStorage.user2 = JSON.stringify({name: 'qianyin'})
      
    • 通过浏览器(chrome)控制台查看数据

  • 获取数据(它们的方式都是有一样的,就以sessionStorage为例)

    • 通过 getItem 获取数据

      sessionStorage.getItem('user')
      
    • 通过对象的形式获取数据

      sessionStorage.user
      
    • 通过 length 属性存储数量

      sessionStorage.getItem('user')
      
  • 移除数据(它们的方式都是有一样的,就以sessionStorage为例)

    • 通过 removeItem 移除指定数据

        sessionStorage.removeItem('user');
      
    • 通过对象的形式移除指定数据

        delete sessionStorage.user
      
    • 移除当前作用域下所有数据

        sessionStorage.clear();
      

2.3 IndexedDB

游览器提供的本地数据库

  • 特点

    • 键值对存储,IndexDB内部采用对象仓存放数据库,每条数据对应一个主键,主键唯一,否则报错
    • 异步操作
    • 支持事务,操作中某步骤发生错误后整个事务取消,数据库回滚至事务之前的状态
    • 同源策略,网页只能访问自身域名下的数据库,而不能访问跨域的数据库
    • 存储空间大,>= 250MB
    • 支持二进制,既可以存储字符串,也可以存储二进制

2.4 Web SQL

HTML5本地数据库

  • 使用场景:适用于移动端,尤其是Hybrid应用

  • 特点

    • 事务事件
    • 查询数据,返回数据类型正确
    • 被W3丢弃的规范,但被广泛使用

三、HTTP缓存

参考:中高级前端工程师都需要熟悉的技能--前端缓存

  • 作用(优点):

    • 减少冗余的数据请求,节省网络带宽。过多的带宽消耗会增加运营成本,当Web缓存副本被使用时,只会产生极小的网络流量,可以有效的降低运营成本。
    • 节省网络资源,降低服务器压力。在发起请求时,会根据缓存机制决定是直接使用副本还是重新请求。从而减少对源服务器的请求,间接降低服务器的压力
    • 减少网络延迟,加快打开速度。直接取缓存的副本速度会重新请求的速度快,从视觉效果上,能明显加快页面打开速度,达到更好的用户体验。
  • 资源缓存位置: 浏览器获取缓存的顺序为 Service Worker Cache、Memory Cache、Disk Cache

    • from Memory Cache: 是把资源存在内存中,当进程退出时(关闭浏览器),内存中的数据会被清空,下次访问要执行别的缓存策略。
    • from disk cache: 是把资源缓存在磁盘中,进程退出时不受影响,下次访问可以继续执行此次缓存策略。
    • Sevice Worker Cache(https): 开发者认为存储的永久性存储,用于离线缓存的处理
  • 缺点

    • 占内存(有些缓存会被存在内存中)
  • 缓存策略

    • 强缓存。不需要发请求询问服务器直接取本地缓存资源。
    • 协商缓存。需要发请求询问服务器,命中后返回304,取本地缓存资源,没有命中服务器就会返回最新资源。

    【相同点】 :使用的都是浏览器缓存到本地的资源 【不同点】 :强缓存不会发起网络请求,协商缓存会发起网络请求

3.1 强制缓存(强缓存)

  • 什么是强缓存 主要是浏览器自行判断资源是否过期,如果不过期则直接使用缓存的资源(强缓存命中),不再进行网络请求。
  • 强缓存的实现 利用Expires或者Cache-Control这两个http response header实现的,它们都用来表示资源在客户端缓存的有效期。

3.1.1 Expires(优先级最低)

  • http1.0 用于缓存管理的header字段,由服务器返回,用GMT格式的字符串表示
  • 值表示一个资源过期的时间,描述的是绝对时间,且该绝对时间属于服务端的时间系统
  • 判断方法:浏览器发起下一次请求时,当前http发起的 请求时间(this http request time) < expires设置的值 ,资源没有过期,缓存命中
  • 弊端:Expires遵循的是服务端的时间系统,而请求时间遵循的是客户端的时间系统,如果两者时间不是一致的,就可能产生误差。例如手动修改本地客户端的时间,那么就可能影响缓存命中结果。
  • 已被弃用!已被 Cache-Control 替代!在不支持 Cache-Control 的情况下还是可以继续使用这个属性的

3.1.2 Cache-Control(优先级第二)

为了解决Expires因为客户端和服务器端时间不统一带来的问题,HTTP 1.1 提出了新的header 也就是 Cache-Control ,这个字段使用相对时间,进行比较的时候用的都是客户端的时间,相对来更有效与安全。

  • http1.1

  • 允许你定义响应资源应该何时、如何被缓存以及缓存多长时间的组成

  • 值是一个相对时间,以秒为单位,用数值表示。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从本地缓存读取,不与服务器做任何交互。

  • 判断方法:浏览器发起下一个请求时,当前HTTP发起的 请求时间(this http request time) < (last http request time + cache-control 设置的值),资源没有过期,缓存命中。

    cache-control: max-age=315360000
    
  • 字段值取值

    • no-cache: 请求头/响应头。强制客户端向服务器发送请求。这个值不是禁止客户端或代理服务器缓存响应(表示禁止强缓存,强制进行协商缓存)
    • no-store: 请求头/响应头。禁止一切缓存。客户端和代理服务器都不能缓存响应(表示禁止任何缓存策略)
    • max-age: 请求头/响应头。设置资源(representations)可以被缓存多长时间,单位是秒,时间是相对于请求的时间(决定客户端资源被缓存多久)
    • no-transform: 请求头/响应头。代理不可更改媒体类型。
    • cache-extension: 请求头/响应头。新指令标记(token)。
    • s-maxage: 响应头。和max-age同理,只不过是针对代理服务器缓存而言(决定服务器缓存的时长)
    • private: 响应头。不能被代理服务器缓存(表示资源只能被浏览器缓存)
    • public: 响应头。响应可以被任何缓存区缓存(表示资源可以被浏览器缓存也可以被代理服务器缓存)
    • must-revalidate: 响应头。在缓存过期前可以使用,缓存过期以后必须向服务器验证。
    • proxy-revalidate: 响应头。要求中间缓存服务器对缓存的响应有效性需再次确认(代理服务器需要发送请求给服务器端确认资源有效性,不能直接返回缓存)
    • only-if-cached: 请求头。从缓存中获取资源
    • min-fresh: 请求头。单位:秒,期望在指定的时间内,响应仍有效
    • max-stale: 请求头。单位:秒, 接受已过期的响应
  • no-cache 与 no-store 的区别

    • no-cache: 强制进行协商缓存
    • no-store: 禁止所有的缓存策略
    • 是一组互斥属性,不能同时出现在 cache-control 中
  • public 与 private 决定资源是否可以在代理服务器进行缓存。默认值是 private

    • public: 表示资源在客户端和代理服务器都可以被缓存
    • private: 表示资源只能在客户端被缓存,拒绝资源在代理服务器缓存
    • 是一组互斥属性,不能同时出现在 cache-control 中
  • max-age 与 s-maxage

    • max-age: 表示的是资源在客户端缓存时长
    • s-maxage: 表示的是资源在代理服务器可以缓存时长。必须和 public 一起使用。
    • 不互斥,可以同时使用

参考: - 图文讲解 Cache-Control 浅显易懂 - Cache-Control_MDN

3.1.3 Pragma(优先级最高)

只有一个值就是 no-cache,含义等同于 Cache-Control 取值为 no-cache,表示禁止强缓存,强制客户端发送http请求给客户端。

  • Pragma在HTTP 响应中的行为没有确切规范,所以尽量少用

参考:

3.2 协商缓存

  • 什么是协商缓存 在强缓存阶段无法命中的情况下,浏览器发起请求,询问服务器是否可以使用本地缓存资源,如果服务器检查发现浏览器本地的资源没有过期,则返回304告诉浏览器可以继续使用本地的缓存资源(协商缓存命中)。就是需要服务器确定是否使用本地缓存资源的缓存策略。

  • 协商缓存方案

    • Last-Modified / If-Modified-Since
    • Etag / If-None-Match

3.2.1 Last-Modified / If-Modified-Since

  • 资源最后的修改时间

  • 缓存实现方式: 1)首先需要在服务器端读出文件修改时间 2)将读出来的修改时间赋给响应头的Last-Modified字段 3)最后设置 Cache-control:no-cache

  • 浏览器发送请求时,会将上次响应头中的 Lasr-Modified 赋值给本次请求头中的 If-Modified-Since 字段。服务端中接收到请求之后,会将这个字段和当前资源最后的修改时间做对比,

    • 如果 If-Modified-Since(上一次资源修改时间) < 服务器上资源的最后修改时间,则说明当前资源被修改过了,服务端需要返回新的资源给服务端,此时响应200,返回正常的响应。同时这次响应会返回新的 Last-Modified 值,用于更新浏览器缓存
    • 如果 If-Modified-Since(上一次资源修改时间) ≥ 服务器上资源的最后修改时间,则说明没有修改过资源,则返回304状态码,不会返回资源内容。
  • 弊端: 1)短时间内资源发生了变化,这个字段并不会发生变化,缓存命中可能失效。文件修改时间记录的最小单位是秒,而文件是在几百毫秒内完成修改,那么文件修改时间不会改变,这样,即使文件内容修改了,依然不会返回新的文件 2)如果出现了服务器资源因为反复修改,但资源内容并没发生变化,此时浏览器再次请求服务器,实际上应该认为缓存命中(实际内容没有变化),但是此时通过该字段的比较会导致缓存命中失效。

3.2.2 Etag / If-None-Match

文件指纹: 根据文件内容计算出的唯一哈希值。文件内容一旦改变则指纹改变。

  • Etag是Last-Modified的补充方案(为了解决Last-Modified的缓存命中问题,可以通过Etag来管理协商缓存命中)
  • 该字段是当前资源在服务器的唯一标识(生成规则由服务器决定),是基于文件内容进行编码的,如果文件内容不发生变动,那么该表示不会发生变更
  • 服务端收到响应以后,根据当前资源内容重新生成一份Etag,比较该值和If-None-Match是否相等,相等则返回304,不相等则返回200和正常响应。但同Last-Modified 的区别在于即使服务器重新生成的Etag字段和原来的没有变化,但是因为重新生成了,304响应中同样会返回Etag字段。
  • 弊端: 1)ETag需要计算文件指纹这样意味着,服务端需要更多的计算开销。。如果文件尺寸大,数量多,并且计算频繁,那么ETag的计算就会影响服务器的性能。显然,ETag在这样的场景下就不是很适合。 2)ETag有强验证和弱验证,所谓将强验证,ETag生成的哈希码深入到每个字节。哪怕文件中只有一个字节改变了,也会生成不同的哈希值,它可以保证文件内容绝对的不变。但是,强验证非常消耗计算量。ETag还有一个弱验证,弱验证是提取文件的部分属性来生成哈希值。因为不必精确到每个字节,所以他的整体速度会比强验证快,但是准确率不高。会降低协商缓存的有效性。

四、DNS缓存

参考:DNS缓存

  • 什么是DNS

    • DNS概念:

      • DNS:Domain Name System ,即域名系统。

      • DNS是一个由分层的DNS服务器实现的分布式数据库

        整个DNS系统由分散在世界各地的很多台DNS服务器组成,每台DNS服务器上都保存了一些数据,这些数据可以让我们最终查询到主机名对应的IP。

        什么是分布式:这个世界上没有一台DNS服务器拥有因特网上所有主机的映射,每台DNS只负责部分映射

      • DNS是一个使得主机能够查询分布式数据库的应用层协议.

        用户端发送一个请求,其中包含我们要查询的主机名,它就会给我们返回这个主机名的IP。

        DNS协议是运行在UDP协议之上,使用端口号53.

    • DNS服务器

      示例:www.baidu.com 的完整写法是 www.baidu.com. 。最后的那个. 是根域名;com是顶级域名。

      DNS的层次结构:根DNS服务器 - 顶级域DNS服务器 - 权威DNS服务器。

      • 根DNS服务器

        • 作用:管理它的下一级,也就是顶级域DNS服务器。
      • 顶级域DNS服务器(TLD)

        • 作用:提供了它的下一级,也就是权威DNS服务器的IP地址
        • 常见的顶级域名:com、cn、org、edu等
      • 权威DNS服务器

        • 作用:返回 主机 - IP 的最终映射
    • DNS缓存的作用:

      • 为了更快的拿到IP地址
  • 本地DNS服务器 严格讲,本地DNS服务器并不属于DNS的层次结构,但是它对DNS层次结构至关重要。

    • 什么是本地服务器:每个ISP都有一台本地DNS服务器,比如一个居民区的ISP、一个大学的ISP、一个机构的ISP,都会有一台或多台本地DNS服务器。当主机发出DNS请求时,该请求被发往本地DNS服务器,本地DNS服务器起着代理的作用,并负责将该请求转发到DNS服务器层次结构中。
  • DNS解析

    • 简单的说,通过域名,最终得到该域名对应的IP地址的过程叫做域名解析(或主机名解析)。
    www.baidu.com (域名) - DNS解析 -> 111.222.33.444 (IP地址)
    
    • DNS查询过程:

      1. 主机m.n.com向它的本地DNS服务器发送一个DNS查询报文,其中包含期待被转换的主机名a.b.com
      2. 本地DNS服务器将该报文转发给DNS服务器;
      3. 该根服务器注意到com前缀,便向本地DNS服务器返回com对应的顶级域DNS服务器(TLD)的IP地址列表。(意思是,不知道a.b.com的IP,不过这些TLD服务器可能知道,你去问他们吧~)
      4. 本地DNS服务器则向其中一台TLD服务器发送查询报文;
      5. 该TLD服务器注意到b.com的前缀,便向本地DNS服务器返回权威DNS服务器的IP地址。(意思是,不知道a.b.com的IP,不过这些权威服务器可能知道,你去问问那他们吧~)
      6. 本地DNS服务器又向其中一台权威服务器发送查询报文;
      7. 终于,该权威服务器返回了a.b.com的IP地址
      8. 本地DNS服务器将a.b.com根IP地址的映射返回给主机m.n.comm.n.com就可以用该IP向a.b.com发送请求啦~
    • DNS解析过程:

      • 1)首先搜索浏览器自身的DNS缓存,如果存在,则域名解析到此为止;
      • 2)如果浏览器自身的缓存里没找到对应的条目,那么会尝试读取操作系统的hosts文件,查看是否存在对应的映射关系,如果存在,则域名解析到此为止;
      • 3)如果本地hosts文件不存在映射关系,则查找本地DNS服务器,如果存在,域名解析到此为止;
      • 4)如果本地DNS服务器未找到的话,它就会向根服务器发出请求,进行递归查询。
  • DNS缓存分类

    • 浏览器DNS缓存
    • 应用程序DNS缓存
    • 操作系统DNS缓存
    • DNS缓存服务器

五、CDN缓存

CDN: Content Delivery Network,内容分发网络

CDN缓存

关于CDN缓存,在浏览器本地缓存失效后,浏览器会向CDN边缘节点发起请求。类似浏览器缓存,CDN边缘节点也存在着一套缓存机制。CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的

Cache-control: max-age   //后面会提到
复制代码

的字段来设置CDN边缘节点数据缓存时间。

当浏览器向CDN节点请求数据时,CDN节点会判断缓存数据是否过期,若缓存数据并没有过期,则直接将缓存数据返回给客户端;否则,CDN节点就会向服务器发出回源请求,从服务器拉取最新数据,更新本地缓存,并将最新数据返回给客户端。 CDN服务商一般会提供基于文件后缀、目录多个维度来指定CDN缓存时间,为用户提供更精细化的缓存管理。

CDN优势

  1. CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。
  2. 大部分请求在CDN边缘节点完成,CDN起到了分流作用,减轻了源服务器的负载。

参考