宏观视角下的浏览器

393 阅读11分钟

Chrome架构:只打开一个页面,为什么有4个进程?

单进程浏览器

单进程浏览器是指浏览器的所有功能模块都运行在一个进程里面,这些模块包含了网络、插件、JavaScript运行环境、渲染引擎和页面等。这么多模块运行在一个进程里面,容易不稳定、不安全、不流畅。

多进程浏览器

chrome进程架构图

最新的Chrome浏览器包括:1个浏览器(Browser)主进程、1个GPU进程、1个网络进程、多个渲染进程和多个插件进程。

浏览器主进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。

GPU进程:主要负责页面UI绘制。

网络进程:主要负责页面的网络资源加载。

渲染进程:核心任务是将HTML/CSS/JS转化为用户可以与之交互的网页,排版引擎和V8都运行在该进程中。默认情况,Chrome会给每个tab创建一个渲染进程(同一站点除外)。出于安全考虑,渲染进程运行在沙箱模式下。

插件进程:负责插件的运行。插件是容易崩溃的,单独隔离避免插件崩溃对浏览器和页面造成影响。

同站和同源

image.png

同站 = 相同协议 + 相同根域名

同源 = 相同协议 + 相同主机 + 相同端口号

TCP协议:如何保证页面文件能被完整送达浏览器?

互联网中的数据是通过数据包来传输的,如果发送的数据很大,那么这条数据就会拆分为很多小数据包来传输。

IP:把数据包送达目的主机

数据包要在网络上进行传输,就要符合网际协议(Internet Protocol)标准。计算机的地址就称为IP地址,访问任何网站实际上只是你的计算机向另外一台计算机请求信息

IP网络三层传输模型

一个数据包从主机A到主机B的过程:

  1. 上层将数据包传给网络层
  2. 网络层再将IP头加到数据包上,组成新的IP数据包,然后交给底层
  3. 底层通过物理网络将数据包传给B主机
  4. 数据包被解开,拆开IP头,将数据交给上层

UDP:把数据包送达应用程序

IP是非常底层的协议,只负责把数据包传给对方电脑,但是具体应用程序却不清楚。因此,需要基于IP之上开发能和应用打交道的协议,最常见的是用户数据包协议(User Datagram Protocol),即UDP

UDP网络四层传输模型

它会在传输层多一点步骤:

  1. 在主机A,从上层传到传输层,传输层会在数据包前面附加上UDP头,组成新的UDP数据包,再传给网络层
  2. 在主机B,从网络层传到传输层,传输层会拆开UDP,根据UDP中所提供的端口号,将数据传递上层

在使用UDP发送数据时,会有各种因素导致出错,虽然UDP可以校验数据是否正确,但是对于错误的数据包,UDP不提供重发机制,只丢弃当前包,而且UDP在发送之后也无法知道是否能达到目的地。

UDP不能保证数据可靠性,但是传输速度非常快。所以UDP会应用在一些关注速度、但不那么严格要求数据完整性的领域,如在线视频、游戏互动等。

TCP:把数据包完整地送达应用程序

对于浏览器请求等要求数据可靠性的应用,UDP带来两个问题:

  1. 数据包在传输中容易丢失
  2. 大文件被拆分为很多小数据包,经过不同路由,在不同时间到达接收端,UDP不知道如何组装,将数据包还原为文件

于是,我们引入TCP。传输控制协议(Transmission Control Protocol)即TCP,是一种面向连接的、可靠的、基于字节流的传输层通信协议。相对于UDP,有以下两个特点:

  1. 对于数据包丢失的情况,TCP提供重传机制
  2. TCP引入了数据包排序机制,保证把乱序数据包组合成一个完整的文件

TCP网络四层传输模型

TCP的连接过程包括“建立连接”、“传输数据”、“断开连接”。

TCP连接的生命周期

TCP为了保证数据传输的可靠性,牺牲了数据包的传输速度,因为“三次握手”和“数据包校验机制”等把传输过程中的数据包的数量提高了一倍。

HTTP请求流程:为什么很多站点第二次打开速度会很快?

HTTP协议,是建立在TCP连接基础之上的。HTTP是一种允许浏览器向服务器获取资源的协议,是Web的基础。它也是浏览器使用最广的协议。

浏览器使用HTTP协议作为应用层协议,用来封装请求的文本信息,并使用TCP/IP作为传输层协议将它发到网络上。HTTP的内容是通过TCP的传输数据阶段来实现的。

HTTP和TCP

浏览器发起HTTP请求流程

  1. 构建请求
  2. 查找缓存,浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术。它可以缓解服务器的压力,提升性能;对于网站来说,缓存是实现快速资源加载的重要组成部分。
  3. 准备IP地址和端口DNS(Domain Name System)域名系统,负责把域名和IP地址做一一映射关系浏览器提供了DNS数据缓存服务在第一步浏览器会请求DNS返回域名对应的IP,如果某个域名被解析过,浏览器会缓存解析的结果供下次查询使用,这样也会减少一次网络请求。
  4. 等待TCP队列,Chrome有个机制,同一域名同时最多只能建立6个TCP连接。同一域名有10个请求的话,其中4个会进入排队等待状态
  5. 建立TCP连接
  6. 发送HTTP请求

服务端处理HTTP请求

  1. 返回请求
  2. 断开连接,保持TCP连接可以省去下次请求时需要建立连接的时间,提升资源加载速度。
  3. 重定向,比如打开一个地址,返回状态码301,就是告诉浏览器我需要重定向到另外一个网址,而需要重定向的网址正是包含在响应头的Location字段中。

为什么很多站点第二次打开速度会很快

主要原因是第一次加载页面过程中,缓存了一些耗时的数据。DNS缓存和页面资源缓存这两块数据会被浏览器缓存

缓存查找流程

如何保持登录状态

如果服务器发送的响应头内有Set-Cookie的字段,那么浏览器就会将该字段的内容保存到本地。当下次客户端再往该服务器发送请求时,客户端会自动在请求头中加入Cookie值后再发送出去。服务器端发现客户端发送过来的Cookie后,会去检查究竟是从哪一个客户发来的连接请求,然后对比服务器的记录,最后得到该用户的状态信息。

实际项目中,可以在LocalStorage里面记录一些用户信息。

导航流程:从输入URL到页面展示,这中间发生了什么?

输入URL到页面展示

总结起来:

  1. 用户输入,根据输入关键字判断是搜索内容还是请求URL,组装协议,合成完整URL
  2. URL请求,这里会查找本地缓存看是否有资源。有的话,就直接返回;没有,就建立DNS解析,请求IP,建立连接,将相关信息添加到请求头,向服务器发送请求
  3. 根据服务器的返回响应,网络进程开始解析,根据状态码来判断是否需要重定向,如果需要,读取重定向地址,发起请求。根据响应的数据类型浏览器做出不同处理:如果是下载类型,该请求就会提交给浏览器的下载管理器,该URL请求的导航流程就结束;如果是HTML,就要准备渲染进程
  4. 如果是同一站点相同协议和根域名),就复用父页面的渲染进程,否则新开一个渲染进程;
  5. 渲染进程准备好以后,提交“文档”(URL请求的响应体数据),和网络进程建立传输数据,传输完成后,浏览器进程收到“确认提交”信息,这时,更新浏览器的界面状态,包括地址栏、前进后退、Web页面等
  6. 渲染进程开始进行页面渲染,这个渲染过程,就是将HTML、CSS、JS变成页面,参考渲染流程
  7. 渲染完成后,通知浏览器进程,页面加载完成,停止标签的加载动画

渲染流程:HTML、CSS和JS,是如何变成页面的?

一个完整的渲染流程大致为:

  1. 渲染进程将HTML内容转换为能够读懂的DOM树结构
  2. 渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算DOM节点的样式
  3. 创建布局树,并计算元素的布局信息
  4. 对布局树进行分层,生成分层树
  5. 为每个图层生成绘制列表,并将其提交到合成线程
  6. 合成线程将图层分为图块,并在光栅化线程池中将图块转化为位图
  7. 合成线程发送绘制图块命令DrawQuad给浏览器进程
  8. 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上

渲染流水线

构建DOM树

浏览器无法直接理解和使用HTML,需要将其转化成浏览器可以理解的DOM树。

样式计算

根据CSS文本,执行转换操作,转化成styleSheets,并对属性值进行标准化,准换成渲染引擎容易理解、标准化的计算值。然后再去计算节点的具体样式(CSS继承,样式层叠)

创建布局树

在显示之前,构建一棵只包含可见元素的布局树,接下来就去计算节点的坐标位置。

分层

页面中有时候会有很复杂的效果,比如3D转换、页面滚动等。渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树。这些图层按照一定的顺序叠加在一起,就形成了最终的页面。通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

  1. 拥有层叠上下文属性的元素会被提升为单独的一层。如:z-index、position、filter、opacity等
  2. 需要剪裁(clip)的地方也会被创建图层

可以通过Chrome的开发者工具的“Layers”,查看分层情况。

图层绘制

渲染引擎会把图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。

绘制列表

image.png

栅格化操作

绘制列表只是用来记录绘制的顺序和绘制指令的列表,真正绘制操作是由渲染引擎中的合成线程来完成的。

合成线程会将图层划分为图块,按照视口附件的图块有限生成位图,实际生成位图的操作是由栅格化(raster)来执行。所谓栅格化,是指将图块转换为位图。该过程会使用GPU来加速生成,生成的位图被保存在GPU内存中。

GPU栅格化.png

合成和显示

一旦所有的图块都被栅格化,合成线程就会生成绘制图块的命令 -- DrawQuad,然后将命令提交给浏览器进程。浏览器进程里的 viz 组件接受命令,并根据命令将内容绘制到内存中,最后显示在屏幕上。

重排

更新了元素的几何属性,需要更新完整的渲染流水线,开销最大。

image.png

重绘

更新元素的绘制属性,进入绘制阶段,不会引起几何位置变换,布局阶段不会执行。相较于重排,省去了布局和分层阶段,执行效率会比重排操作高。

image.png

直接合成阶段

渲染引擎跳过布局和绘制,只执行后续的合成操作,这个过程就是合成。

image.png

避开重排和重绘,直接在非主线程上执行合成动画操作,相对于重绘和重排,合成能大大提升绘制效率。