Fork me on GitHub

前端性能优化资料整理

前端性能优化资料整理

页面性能差的直接后果是用户需要等待,而等待,尤其是不确定要多长时间的等待会给用户带来焦虑,为了尽早的结束这种焦虑,除非访问网页是刚需,用户通常会选择直接关闭页面。从实际数据来看,性能差是页面高跳出率的重要原因之一。以下内容部分来自网络并且结合个人理解持续更新


浏览器的基本知识以及基础渲染原理

1.浏览器应该有的功能

  • 网络:

浏览器通过网络模块来下载各式各样的资源,例如html文本;javascript代码;样式表;图片;音视频文件等。
网络部分本质上十分重要,因为它耗时长,而且需要安全访问互联网上的资源。

  • 资源管理:

从网络下载,或者本地获取到的资源需要有高效的机制来管理它们。
例如如何避免重复下载,资源如何缓存等

  • 网页浏览:

这是浏览器的核心也是最基本的功能,最重要的功能。
如何将资源转变为可视化的结果。

其他功能

  • 多页面管理
  • 插件与管理
  • 账户和同步
  • 安全机制
  • 开发者工具

浏览器的主要功能可以总结为:

将用户输入的url转变成可视化的图像
1.从url到dom树
2.从dom树到可视化图像
这两个过程之间的关系并没有那么明确,我们可以统称这两个过程为页面的渲染

2.浏览器的渲染引擎

在浏览器中有一个最重要的模块,它主要的作用是将页面转变为可视化的图像结果。
这个模块就是浏览器内核,通常它也被称为渲染引擎。

  • IE –> Trident
  • Safari –> WebKit
    • WebKit本身主要是由两个引擎构成的,
      一个正是渲染引擎“WebCore”,
      另一个则是javascript解释引擎“JSCore”,
      它们均是从KDE的渲染引擎KHTML及javascript解释引擎KJS衍生而来。
  • Chrome –> WebKit的分支引擎 –> Blink
    • 在13年发布的Chrome 28.0.1469.0版本开始,Chrome放弃Chromium引擎转
      而使用最新的Blink引擎(基于WebKit2——苹果公司于2010年推出的新的WebKit引擎),
      Blink对比上一代的引擎精简了代码、改善了DOM框架,也提升了安全性。
  • Opera
    • 旧版Opera 4至6版本 –> Elektra排版引擎
    • Opera7.0 –> Presto渲染引擎
    • Opera在2013年2月宣布放弃Presto:
    • 采用Chromium引擎;
    • 又转为Blink引擎;
  • Firefox –> Gecko

3.进程&线程

  • 进程: 程序的一次执行, 它占有一片独有的内存空间.是操作系统执行的基本单元。

一个进程中至少有一个运行的线程: 主线程, 进程启动后自动创建
一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
一个进程内的数据可以供其中的多个线程直接共享,多个进程之间的数据是不能直接共享的

  • 线程:是进程内的一个独立执行单元,是CPU调度的最小单元。程序运行的基本单元

线程池(thread pool): 保存多个线程对象的容器, 实现线程对象的反复利用

JS引擎是单线程运行的(异步与同步)

4.现代浏览器的多进程多线程模型

  • 1.不堪回首的过去:

    • 当你通过浏览器打开很多页面的时候,如果其中一个页面不响应了或者崩溃了,
    • 那么随之而来的将会是更不幸的事情,你开打的所有页面都会得不到响应,
    • 最让人不能忍受的是,其中的一些页面可能还包含了未保存或者未发送的信息
  • 2.浏览器产商如何解决

    • 采用多进程模型,该模型可以带来的好处
    • ①.避免因单个页面的不响应或者崩溃影响整个浏览器的稳定性
    • ②.当第三方插件崩溃时,也不会影响整个浏览器的稳定性
    • ③.安全
  • 3.浏览器到底有些什么进程

    • ①.Browser进程:
      浏览器的主进程,负责浏览器界面的显示,和各个页面的管理,
      浏览器中所有其他类型进程的祖先,负责其他进程的的创建和销毁
      它有且只有一个!!!!!
    • ②.Renderer进程:
      网页渲染进程,负责页面的渲染,可以有多个
      当然渲染进程的数量不一定等于你开打网页的个数
    • ③.NPAPI插件进程
    • ④.Pepper插件进程
    • ⑤.GPU进程
  • 移动设备的浏览器可能不太一样:

    • Android不支持插件,所以就没有插件进程
    • GUP演化成了Browser进程的一个线程
    • Renderer进程演化成了操作系统的一个服务进程,它仍然是独立的
  • 4.每个进程内部又有很多线程

    • 多线程的目的主要是保持用户界面的高度响应
    • 例如:为了不让Browser进程的UI线程被其他耗时的操作(数据库读写,本地文件读写)所阻塞,
    • 那么我们就把这些操作放到分线程中去处理
    • 在Renderer进程中,为了不让其他操作阻止渲染线程的高速执行,我们通常会将渲染过程管线化,
      利用计算机的多核优势,让渲染的不同阶段在不同的线程中执行

管线化一次处理多个请求,不需要一个接一个地等待响应


渲染引擎

1.主要引擎

一个渲染引擎主要包括:HTML解析器,CSS解析器,布局layout模块,javascript引擎,绘图模块

  • HTML解析器:
    • 解释HTML文档的解析器,主要作用是将HTML文本解释成DOM树。
  • CSS解析器:
    • 级联样式表的解析器,它的作用是为DOM中的各个元素对象计算出样式信息,为布局提供基础设施
  • 布局(layout):
    • 在DOM创建之后,Webkit需要将其中的元素对象同样式信息结合起来,
      计算他们的大小位置等布局信息,形成一个能表达这所有信息的内部表示模型
  • Javascript引擎:
    • 使用Javascript代码可以修改网页的内容,也能修改css的信息,javascript引擎能够解释javascript代码
      并通过DOM接口和CSSOM接口来修改网页内容和样式信息,从而改变渲染的结果。
  • 绘图模块:
    • 使用图形库将布局计算后的各个网页的节点绘制成图像结果

以上这些模块依赖很多其他的基础模块,包括要使用到网络 存储 2D/3D图像 音频视频解码器 和 图片解码器。
所以渲染引擎中还会包括如何使用这些依赖模块的部分。

2.渲染过程

1.网页URL到构建DOM树的整个过程

  • a. 当用户输入URL的时候,Webkit调用资源加载器加载URL对应的网页
  • b. 加载器依赖网络模块建立连接,发送请求并接收答复
  • c. Webkit接收到各种网页或者资源的数据,其中某些资源可能是同步的或异步获取的
  • d. 网页被交给HTML解析器转变一系列的词语(Token)
  • e. 解析器根据词语构建节点(node),形成DOM树
  • f. 如果节点需要依赖于其他资源,
    • 例如js css 图片 视频等,调用资源加载器来加载他们,但是这些都是异步的,不会阻碍dom树的继续创建;
    • 也就是说资源加载是并发的,但这个并发度受到域名的限制,所有一般我们会在一个项目中配多个CDN地址
    • 来尽量使浏览器处于一个并发饱满的状态。顺序执行 并发加载
    • 如果资源是css的话
      • 调用CSS解析器解释将CSS解释成内部表示结构( CSSDOM)
    • 如果资源是javascript的话
      • 调用Javascript引擎解释并执行,
  • g. 阻塞(下面单独说明)

在上述的过程中,网页在加载和渲染过程中会发出“DOMContentloaded”和“onload”事件
分别在DOM树构建完成之后,以及DOM树构建完并且网页所依赖的资源都加载完之后发生、
因为某些资源的加载并不会阻碍DOM树的创建,所以这两个事假多数是不同时发生的

2.阻塞

  • css阻塞
    • css 在head中引入会阻塞页面的渲染
    • 为什么?style被html解析器当做html元素解析了
    • 避免闪屏现象:使用link的外联样式表
    • css 阻塞js的执行,不阻塞js等其他资源的加载
    • 为什么?↓

脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,
脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,
但事实上非常普遍。Firefox 在样式表加载和解析的过程中,会禁止所有脚本。
而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本。

  • js阻塞

    • 直接引入的js会阻塞页面的渲染
    • 为什么?Javascript代码可能会修改DOM树的结构
    • js顺序执行,阻塞后续js逻辑的执行,不阻塞js等其他资源的加载
    • 为什么?维护依赖关系
  • 预解析

WebKit 和 Firefox 都进行了这项优化。在执行js脚本时,其他线程会解析文档的其余部分,
找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,
从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;
预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。

3.从DOM树到可视化图像

  • 1.CSS文件被CSS解析器解释成内部表示结构( CSSDOM)
  • 2.CSS解析器工作完成之后,在DOM树上附加解释后的样式信息,这就是RenderObject树
  • 3.RenderObject在创建的同时,Webkit会根据网页的结构创建RenderLayer,同时构建一个绘图上下文
  • 4.根据绘图上下文生成最终的图像(这一过程需要依赖图形库)

上面介绍的是一个完整的渲染过程,但现代网页很多都是动态的,这意味着在渲染完成之后,由于网页的动画或者用户的交互,
浏览器其实一直在不停地重复执行渲染过程。(重绘重排),以上的数字表示的是基本顺序,这不是严格一致的,
这个过程可能重复也可能交叉


图层&重绘重排

1.css图层

浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。

在渲染DOM的时候,浏览器所做的工作实际上是:

  • 1.获取DOM后分割为多个图层
  • 2.对每个图层的节点计算样式结果 (Recalculate style –> 样式重计算)
  • 3.为每个节点生成图形和位置 (Layout –> 重排,回流)
  • 4.将每个节点绘制填充到图层位图中 (Paint –> 重绘)
  • 5.图层作为纹理上传至GPU
  • 6.符合多个图层到页面上生成最终屏幕图像 (Composite Layers –> 图层重组)

2.css图层创建的条件(以Chrome为基准)

  • 1.拥有具有3D变换的CSS属性
  • 2.使用加速视频解码的video节点
  • 3.canvas节点
  • 4.CSS3动画的节点
  • 5.拥有CSS加速属性的元素(will-change)
  • 6.元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
    你在图层上渲染,有一个z-index较低兄弟元素,而且这个兄弟元素本身就有一个图层则该元素会影响其他兄弟元素产生图层,但是这不是我们想要的,给这个要开启图层的元素比较高的层级就不会影响其他元素

关于will-change(以下内容来自MDN)

  • 不要将 will-change 应用到太多元素上:浏览器已经尽力尝试去优化一切可以优化的东西了。有一些更强力的优化,如果与 will-change 结合在一起的话,有可能会消耗很多机器资源,如果过度使用的话,可能导致页面响应缓慢或者消耗非常多的资源。

  • 有节制地使用:通常,当元素恢复到初始状态时,浏览器会丢弃掉之前做的优化工作。但是如果直接在样式表中显式声明了will-change属性,则表示目标元素可能会经常变化,浏览器会将优化工作保存得比之前更久。所以最佳实践是当元素变化之前和之后通过脚本来切换 will-change 的值。

  • 不要过早应用 will-change 优化:如果你的页面在性能方面没什么问题,则不要添加 will-change 属性来榨取一丁点的速度。 will-change 的设计初衷是作为最后的优化手段,用来尝试解决现有的性能问题。它不应该被用来预防性能问题。过度使用 will-change 会导致大量的内存占用,并会导致更复杂的渲染过程,因为浏览器会试图准备可能存在的变化过程。这会导致更严重的性能问题。
  • 给它足够的工作时间:这个属性是用来让页面开发者告知浏览器哪些属性可能会变化的。然后浏览器可以选择在变化发生前提前去做一些优化工作。所以给浏览器一点时间去真正做这些优化工作是非常重要的。使用时需要尝试去找到一些方法提前一定时间获知元素可能发生的变化,然后为它加上 will-change 属性。

3.重绘(Repaint)

重绘是一个元素外观的改变所触发的浏览器行为,例如改变outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,所以并不一定伴随重排。

  • 需要注意的是,如果图层中某个元素需要重绘,那么整个图层都需要重绘。
  • 比如一个图层包含很多节点,其中有个gif图,gif图的每一帧,都会重回整个图层的其他节点,然后生成最终的图层位图。
  • 所以这需要通过特殊的方式来强制gif图属于自己一个图层(translateZ(0)或者translate3d(0,0,0)
  • CSS3的动画也是一样(好在绝大部分情况浏览器自己会为CSS3动画的节点创建图层)

4.重排(Reflow 回流)

渲染对象在创建完成并添加到渲染树时,并不包含位置和大小信息。计算这些值的过程称为布局或重排

  • “重绘”不一定需要”重排”,比如改变某个网页元素的颜色,就只会触发”重绘”,不会触发”重排”,因为布局没有改变。
  • 但是,”重排”必然导致”重绘”,比如改变一个网页元素的位置,就会同时触发”重排”和”重绘”,因为布局改变了。

5.触发重绘的属性

基本样式 背景样式 盒模型相关
color background outline-color
border-style background-image outline
border-radius background-position outline-style
visibility background-repeat outline-width
text-decoration background-size box-shado

5.触发重排的属性

盒子模型相关属性 定位属性及浮动 改变节点内部文字结构
width top text-align
heigh bottom overflow-y
padding left font-weight
margin righ overflow
display position font-family
border-width float line-height
border clear vertival-align
min-height white-space

6.常见的触发重排的操作

重排的成本比重绘的成本高得多的多。DOM Tree 里的每个结点都会有重排方法,
一个结点的重排很有可能导致子结点,甚至父点以及同级结点的 重排。在一些高性能的电脑上也许还没什么,
但是如果重排发生在手机上,那么这个过程是非常痛苦和耗电的。

下面这些动作有很大可能会是成本比较高的。

  • 当你增加、删除、修改 DOM 结点时,会导致重绘重排
  • 当你移动 DOM 的位置
  • 当你修改 CSS 样式的时候。
  • 当你 Resize 窗口的时候(移动端没有这个问题)
  • 当你修改网页的默认字体时。
  • 获取某些属性时(width,height…)

!注:display:none 会触发 重排,而 visibility:hidden 只会触发 重排,因为没有发生位置变化。

7.优化

如果我们需要使得动画或其他节点渲染的性能提高,需要做的就是减少浏览器在运行时所需要做的工作(减少1234中的步骤)

  1. 计算需要被加载到节点上的样式结果(Recalculate style–样式重计算)
  2. 为每个节点生成图形和位置(Layout–回流和重布局)
  3. 将每个节点填充到图层中(Paint Setup和Paint–重绘)
  4. 组合图层到页面上(Composite Layers–图层重组)
  • 1.元素位置移动变换时尽量使用CSS3的transform来代替对top left等的操作
    • 变换(transform)和透明度(opacity)的改变仅仅影响图层的组合
  • 2.使用opacity来代替visibility
    • 透明度竟然不会触发重绘?
    • 透明度的改变时,GPU在绘画时只是简单的降低之前已经画好的纹理的alpha值来达到效果,并不需要整体的重绘。
    • !不过这个前提是这个被修改opacity本身必须是一个图层,如果图层下还有其他节点,GPU也会将他们透明化
  • 3.不要使用table布局
    • table-cell
  • 4.将多次改变样式属性的操作合并成一次操作
    • 不要一条一条地修改DOM的样式,预先定义好class,然后修改DOM的className
  • 5.将DOM离线后再修改
    • 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。
    • 如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。
  • 6.利用文档碎片(document.createDocumentFragment())

DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。不过它有一种特殊的行为,该行为使得它非常有用,即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作,尤其是与 Range 接口一起使用时更是如此。

  • 7.不要把某些DOM节点的属性值放在一个循环里当成循环的变量
    当你请求向浏览器请求一些 style信息的时候,就会让浏览器flush队列,比如:
    • 1.offsetTop, offsetLeft, offsetWidth, offsetHeight
    • 2.scrollTop/Left/Width/Height
    • 3.clientTop/Left/Width/Height
    • 4.width,height

当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要flush队列,
因为队列中可能会有影响到这些值的操作。即使你获取元素的布局和样式信息跟最近发生或改变的布局信息无关,
浏览器都会强行刷新渲染队列。

  • 8.动画实现过程中,启用GPU硬件加速
  • 9.为动画元素新建图层,提高动画元素的z-index

8.使用requestAnimationFrame

window.requestAnimationFrame() 方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画
该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。

  • 回调函数会被传入一个参数,DOMHighResTimeStamp,指示requestAnimationFrame() 开始触发回调函数的当前时间

  • 返回值

    • 一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。
      你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。
  • window.cancelAnimationFrame(requestID)

    • 取消一个先前通过调用window.requestAnimationFrame()方法添加到计划中的动画帧请求.
      requestID是先前调用window.requestAnimationFrame()方法时返回的ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//基本使用
var timer;
btn.onclick = function(){
myDiv.style.width = '0';
cancelAnimationFrame(timer);
timer = requestAnimationFrame(function fn(){
if(parseInt(myDiv.style.width) < 500){
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
timer = requestAnimationFrame(fn);//递归调用
}else{
cancelAnimationFrame(timer);
}
});
}

DNS&CDN

1.DNS域名解析服务

DNS服务是和HTTP协议一样位于应用层的协议,它提供域名到IP地址之间的解析服务。

  • 计算机既可以被赋予IP地址,也可以被赋予主机名和域名。
  • 比如www.xxx.com用户通常使用主机名或域名来访问对方的计算机,而不是通过IP地址访问,因为与IP地址的一组纯数字相比,用域名来指定计算机名更符合人类的记忆习惯。
  • 但是要让计算机去理解域名,相对而言就变得困难了,因为计算机更擅长处理一长串数字。
  • 为了解决上述问题,DNS服务应运而生。NDS协议提供通过域名查找IP,或逆向从IP地址反查域名的服务
1
2
chrome://dns/ :查看chrome浏览器上的DNS缓存
ipconfig /displaydns :查看操作系统中的DNS缓存

2.一次完整的请求在网络层面前端需要关注什么?

  • DNS是否可以通过缓存来减少查询IP地址的时间?
    • 可以的本地缓存会大大降低DNS解析事件
  • 网络的请求过程是否走的是最近的网络?
    • 网络的物理距离会影响响应的效率
  • 是否可以少发几次请求?
    • 合并依赖、精灵图等等资源合并操作..
  • 请求体是否可以尽量的小?
    • 压缩图片,压缩代码等等资源压缩操作

3.DNS是否可以通过缓存来减少查询IP地址的时间?

  • DNS的查找是有开销的,通常浏览器查找一个给定的主机名的IP地址需要花费20到120毫秒。
  • 在DNS查找完成之前,浏览器不能从主机那下载任何东西。
  • DNS是可以被缓存起来提高性能的,这种缓存可以发生在网络运营商的服务器上,
    也可以发在本机所处的局域网中,甚至可以发生在本地的操作系统或浏览器中!!
  • 但是我们也知道服务器的IP地址是可变的,缓存会消耗内存,因此不管是哪个级别的
    缓存都应该周期性的清除一下。

DNS域名解析过程

  • 第1步,查找浏览器缓存。
    • 浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果缓存中有,这个解析过程就将结束。
  • 第2步,查找系统缓存
    • 如果用户的浏览器缓存中没有,浏览器会查找操作系统缓存中是否有这个域名对应的DNS解析结果
  • 第3步,查找路由器缓存。
    • 如果系统缓存中也找不到,那么查询请求就会发向路由器,它一般会有自己的DNS缓存。
  • 第4步,查找ISP DNS 缓存。(网络运营商)

运气实在不好,就只能查询ISP DNS缓存服务器了。在我们的网络配置中都会有”DNS服务器地址”这一项,
操作系统会把这个域名发送给这里设置的DNS,也就是本地区的域名服务器,这个专门的域名解析服务器性能都会很好
它们一般都会缓存域名解析结果,当然缓存时间是受域名的失效时间控制的。大约80%的域名解析都到这里就已经完成了,
所以ISP DNS主要承担了域名的解析工作。

  • 第5步,递归搜索
    最无奈的情况发生了, 在前面都没有办法命中的DNS缓存的情况下
    • 1.本地 DNS服务器即将该请求转发到互联网上的根域
    • 2.根域将所要查询域名中的顶级域(即blog.baidu.com中的com)的服务器IP地址返回到本地DNS。
    • 3.本地DNS根据返回的IP地址,再向顶级域(就是com域)发送请求。
    • 4.com域服务器再将域名中的二级域(即blog.baidu.com中的baidu)的IP地址返回给本地DNS。
    • 5.本地DNS再向二级域发送请求进行查询。
    • 6.之后不断重复这样的过程直到本地DNS服务器得到最终的查询结果,并返回到主机。这时候主机才能通过域名访问该网站。

减少DNS查询

  • 1.一个多资源的站点最好使用2到4个不一样的主机来存放服务端资源。
    这是在减少DNS查询和允许高度并行下载之间作出的最好权衡
    (高度并行下载,浏览器一次能并发加载的量是受域名控制的)
  • 2.使用Keep-alive进行持久连接

4.网络的请求过程怎样走最近的网络

网站通常将其所有的服务器都放在同一个地方,当用户群增加时,公司就必须在多个地理位置不同的服务器上部署内容
为了缩短http请求的时间,我们应该把大量的静态资源放置的离用户近一点。

内容发布网络CDN(Content Delivery Networks)
CDN是一组分布在多个不同地理位置的web服务器,用于更加有效的向用户发布内容

基本思路:

尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。
通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,
CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息
将用户的请求重新导向离用户最近的服务节点上。

基础架构:最简单的CDN网络由一个DNS服务器和几台缓存服务器组成

  • 1.当用户点击网站页面上的内容URL,经过本地DNS系统解析,
    DNS系统会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。
  • 2.CDN的DNS服务器将CDN的全局负载均衡设备IP地址返回用户。
  • 3.用户向CDN的全局负载均衡设备发起内容URL访问请求。
  • 4.CDN全局负载均衡设备根据用户IP地址,以及用户请求的内容URL,
    选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求。
  • 5.区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,
    • 选择的依据包括:根据用户IP地址,判断哪一台服务器距用户最近;
    • 根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;
    • 查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。
    • 基于以上这些条件的综合分析之后,
    • 区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的IP地址。
  • 6.全局负载均衡设备把服务器的IP地址返回给用户。
  • 7.用户向缓存服务器发起请求,缓存服务器响应用户请求,将用户所需内容传送到用户终端。
    如果这台缓存服务器上并没有用户想要的内容,而区域均衡设备依然将它分配给了用户,
    那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉到本地。

缓存机制

即便缓存服务器(客户端浏览器)内有缓存文件,也不能保证每次都会使用到对应资源的缓存,这和被缓存资源的有效性有关
列如客户端的要求,缓存的有效期等
当遇上源服务器上的资源更新时,如果还是使用不变的缓存,那就演变成返回更新前的旧资源了

1.客户端自己决定的缓存策略

实体首部字段Expires(响应)

  • 首部字段Expires会将资源失效的日期告知客户端,缓存服务器在接收到含有首部字段Expires的响应后,会以缓存来应答请求。
  • 在Expires字段值指定的时间之前,响应的副本会一直被保存。
  • 当超过指定的时间后缓存服务器在请求发送过来时,会转向源服务器请求资源。
  • 源服务器不希望缓存服务器对资源缓存时,最好在Expires字段内写入与首部字段Date相同的时间值。
  • 但是在首部字段cache-control有指定max-age指令时,比起首部字段Expires,会优先处理max-age指令

通用首部字段cache-control

响应指令 说明
max-age[秒] 响应的最大age值
no-cache 缓存前必须先确认其有效性
no-store 不缓存请求或响应的任何内容

首部字段cache-control有指定max-age指令时,比起首部字段Expires,会优先处理max-age指令

客户端与服务端协商的缓存策略

实体首部字段last-modified(响应)& 请求首部字段if-modified-since

  • 首部last-modified指明资源最终修改的时间,一般来说,这个值就是资源被修改的时间。

  • 首部字段if-modified-since

    • 第一次请求资源时,资源在响应头中种入last-modified字段,并随着响应体一起存到缓存中
    • 下一次需要再发送请求时,请求体中会将上一次修改时间(last-modified)种入if-modified-since字段中带到服务端,它会告知服务器:
      • 若在if-modified-since字段值之后对应的资源都没有更新过则返回304 Not Modified状态码
      • 若在if-modified-since字段值之后 对应的资源有过更新 则希望服务器能处理成功 200
        失败 500

此处的客户端与服务端协商的缓存策略一般与cache-control一块使用。需要在cache-control失效后再走这种缓存策略

缺点:

  • 1.某些服务端没有办法获取精确的修改时间,导致last-modified有问题
  • 2.文件时间修改了,但文件内容却没有变

响应首部字段etag & 请求首部字段if-None-Match

  • 响应首部字段etag:

    • 它可以告知客户端实体标识,它是一种可以将资源以字符串做唯一标识的方式,服务器会为每份资源分配对应的Etag值。
    • 另外当资源更新时,etag的值也需要更新,这个唯一标识的生成没有规定统一的算法,由服务器自行决定
  • 请求首部字段if-None-Match

    • 机制和if-modified-since差不多,当if-None-Match字段与etag不一致时,就告知服务器该处理这请求

此处的客户端与服务端协商的缓存策略一般与cache-control一块使用。需要在cache-control失效后再走这种缓存策略

总结 分级缓存策略

  • 1.200 from cache

这一层由exprise(http1.0)、cache-control(http1.1)控制
cache-control的优先级要高于exprise
当它们没有失效时,浏览器只能访问总结的缓存

  • 2.304状态

这一层由last-modified或etag来控制,
etag优先级比last-modified高
当上一层失效时,用户刷新时浏览器会发请求给服务器,如果服务端没有变化这返回304给浏览器

  • 3.200状态

当上两层都失效时,浏览器会去服务器下载最新的数据