客户端容器

115 阅读25分钟

浏览器架构

浏览器架构演进

  1. 单进程架构:所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等

    • 在早期的浏览器中,所有的功能都在一个进程中完成,包括用户界面、JavaScript执行、CSS渲染和网络请求等。单进程架构的优势是简单、易于实现,但缺点也很明显,如:不稳定(一个标签页崩溃可能导致整个浏览器崩溃)、不安全(攻击者可以利用漏洞来控制整个浏览器)以及性能瓶颈(多个标签页争抢资源,导致性能下降)。
  2. 多进程架构:主进程、网络进程、渲染进程、GPU进程、插件进程

    • 为了解决单进程架构的问题,浏览器厂商开始将浏览器的功能拆分为多个进程。例如,谷歌推出的Chrome浏览器就采用了多进程架构。在这种架构下,浏览器将每个标签页(或每个渲染实例)分配给一个单独的进程,从而实现了进程间的隔离,防止单个网页崩溃影响到其他界面。多进程架构提高了浏览器的稳定性、安全性和性能,但同时也增加了内存消耗
    • 每一个Tab运行在独立的沙盒里面
  3. 面向服务架构:将原来的UI、数据库、文件、设备、网络等,作为一个独立的基础服务

    • 面向服务架构 => 多进程架构升级版本
    • 随着Web平台的发展,浏览器需要处理越来越多的任务,这使得多进程架构变得复杂且难以维护。面向服务架构的出现旨在将浏览器的各个功能模块化,将它们作为独立的服务来运行和维护。在这种架构下,每个服务都可以独立地使用一个或多个进程,根据需求自动扩展或缩减资源。这样可以进一步提高浏览器的稳定性、安全性和性能,同时优化资源利用率

image.png

浏览器架构对比

image.png

浏览器架构 - 任务管理器

在多进程浏览器架构中,任务管理器是一个非常重要的组件。任务管理器负责监控、管理和分配浏览器的各个进程,确保整个浏览器系统的稳定性和性能。

任务管理器主要负责以下几方面的功能:

  1. 进程监控:任务管理器可以实时查看浏览器中的各个进程,包括标签页进程、插件进程和扩展进程等。它可以展示各个进程的资源使用情况,如内存占用、CPU使用率和网络活动等。
  2. 进程管理:任务管理器负责启动和关闭浏览器的进程。当用户打开一个新的标签页或启动一个插件时,任务管理器会创建相应的进程。当标签页被关闭或插件停止运行时,任务管理器会结束相应的进程,以释放系统资源。
  3. 资源分配:任务管理器根据各个进程的资源需求,动态地分配系统资源。例如,对于需要大量CPU资源的进程,任务管理器会分配更多的CPU时间片。同时,任务管理器还可以确保关键进程(如用户界面进程)始终获得足够的资源,从而保证整个浏览器的流畅运行。
  4. 进程隔离:为了提高浏览器的安全性,任务管理器会对各个进程进行隔离。这意味着一个进程无法直接访问另一个进程的内存空间。进程隔离可以防止潜在的安全漏洞影响到整个浏览器系统。
  5. 异常处理:任务管理器负责检测和处理进程中的异常情况。当某个进程发生崩溃或无响应时,任务管理器会尝试自动恢复该进程。如果无法恢复,任务管理器会提示用户关闭相应的标签页或插件,以防止整个浏览器崩溃。

通过任务管理器,浏览器可以有效地监控和管理其各个进程,确保整个系统的稳定性、性能和安全性。不同浏览器的任务管理器实现可能会有所差异,但它们的核心功能和目标都是类似的。在谷歌Chrome浏览器中,用户可以通过按下Shift + Esc快捷键或者在菜单中选择“更多工具” > “任务管理器”来打开任务管理器。

image.png

浏览器架构 - 多进程分工

进程名称进程描述
浏览器(主进程)主要负责页面展示逻辑,用户交互,子进程管理;包括地址栏、书签、前进、后退、收藏夹等
GPU进程负责UI绘制,包含整个浏览器全部UI
网络进程网络服务进程,负责网络资源加载
标签页(渲染进程)控制tab内的所有内容,将HTML、CSS和JavaScript转换为用户可交互的网页
插件进程控制网站运行的插件,比如flash、ModHeader等
其他进程如上图所示:适用程序Storage/Network/Audio Service等

问题思考

  1. 为什么会有单进程架构?

    • 单进程架构在早期的浏览器中出现,当时的Web技术相对较为简单,浏览器需要处理的任务和复杂性远不及现在。在这个背景下,单进程架构具有一些优势:

      1. 简单性:单进程架构更易于实现和维护,因为它不需要处理多进程间的通信和资源分配问题。对于早期的浏览器开发者来说,单进程架构提供了一种快速实现浏览器功能的方法。
      2. 资源消耗:相较于多进程架构,单进程架构在内存和CPU资源消耗方面较低。因为多进程架构中,每个进程都需要占用一定的系统资源,而单进程架构只需要维护一个进程。在计算机硬件资源有限的早期,这一点是非常重要的。
      3. 兼容性:早期的Web技术和标准相对较为简单,单进程架构在兼容性方面表现优秀。因为不需要处理多进程间的兼容性问题,单进程浏览器更容易支持不同的操作系统和硬件平台。

      然而,随着Web技术的发展,浏览器需要处理越来越复杂的任务,如多媒体播放、实时通信和大量的JavaScript代码等。这使得单进程架构的局限性变得越来越明显,包括稳定性、安全性和性能等方面的问题。因此,浏览器厂商开始采用多进程架构和面向服务架构,以适应这些新的挑战。

      单进程架构在早期的浏览器中出现,是因为它在简单性、资源消耗和兼容性方面具有优势。然而,随着Web技术的发展,这种架构的局限性逐渐暴露,导致现代浏览器转向多进程架构和面向服务架构

  2. 面向服务架构是否会替代多进程架构?

    • 面向服务架构在某种程度上可以看作是多进程架构的一种演进和优化。这种架构将浏览器的各个功能模块化,作为独立的服务来运行和维护。这样可以进一步提高浏览器的稳定性、安全性和性能,同时优化资源利用率。

      尽管面向服务架构具有一定的优势,但是它不一定会完全替代多进程架构。这是因为两种架构各自有不同的应用场景和优缺点:

      1. 面向服务架构更适用于复杂的浏览器系统,它可以更好地管理和维护浏览器的各个功能模块。然而,在一些轻量级或特定用途的浏览器中,多进程架构可能仍然是一个合适的选择,因为它相对简单且易于实现。
      2. 面向服务架构的优势在于模块化和灵活性,它可以根据需求自动扩展或缩减资源。但是,这种架构可能需要更多的系统资源和开发维护成本。多进程架构在资源消耗和开发成本方面可能更具优势
      3. 不同浏览器厂商可能会根据自己的产品定位和技术栈选择不同的架构。例如,谷歌Chrome浏览器采用了多进程架构,并在此基础上引入了一些面向服务架构的设计理念。而其他浏览器厂商可能会选择完全基于面向服务架构的实现。

      总之,面向服务架构在某些方面具有优势,但它不一定会完全替代多进程架构。两种架构各自适用于不同的应用场景,浏览器厂商可能会根据实际需求和技术栈选择合适的架构。随着Web技术的不断发展,未来浏览器架构可能会继续演进,以适应更多的需求和挑战。

渲染进程

常见浏览器内核

内核浏览器JS引擎补充说明
TridentIE4-11JScript,Chakra出生于1994年,IE8以前使用JScript引擎,IE9开始使用Chakra引擎
GeckoFirefoxSpiderMonkeyGecko内核主要用在Firefox浏览器上,同时是一个跨平台的内核,支持在Windows、BSD、Linux、Mac OS X中使用
WebkitSafari、Chrome、Android(安卓)浏览器JavaScriptCore由Apple公司技术团队开发,并在2005年开源
BlinkChrome,OperaV8Google:基于Webkit开发的内核,在Vebkit的基础上加入多进程,沙箱等技术,于2013年开源。(NodeJS使用的就是这个引擎)
EdgeEdgeChakra2015年由微软发布,用于Edge浏览器上,由于性能较差,运行不稳定等原因,2018年微软将Edge浏览器内核迁移到Chromium
Trident+Webkit(Blink)国产浏览器QQ、360、搜狗、UC等都有早期银行系统都是在IE上进行开发,想要支持银行系统就切换到Trident内核,想要体验就切到Webkit内核

渲染进程 - 多线程架构

在这里我们涉及到了(多)线程,前面涉及到了(多)进程。这里补充额外知识来说明两者:

  • 进程和线程都是操作系统进行任务管理的基本单位,它们之间有一定的关联性,但也有明显的区别

  1. 进程:

进程是一个独立的程序执行实例,它包括程序的代码、数据以及运行时所需的系统资源(如文件描述符、内存空间等)。每个进程都有自己的独立地址空间,操作系统会为每个进程分配和管理资源。

进程的特点:

  • 独立性:进程具有独立的地址空间,一个进程无法直接访问另一个进程的资源,除非通过进程间通信(IPC)机制。
  • 隔离性:操作系统会为每个进程分配和管理资源,确保它们之间的隔离。这有助于提高系统的稳定性和安全性。
  • 资源开销:创建和维护进程需要消耗一定的系统资源,如内存、CPU时间等。

应用场景:进程通常用于执行独立的、需要隔离的任务。例如,在多进程浏览器中,每个Tab标签页都运行在一个单独的进程中,以实现进程间的隔离和资源管理。

  1. 线程:

线程是进程内的一个执行单元,它共享进程的地址空间和资源,但拥有自己的执行堆栈和程序计数器。一个进程可以包含多个线程,它们可以并发地执行任务。

线程的特点:

  • 轻量级:线程比进程更轻量级,创建和切换线程的开销较小。
  • 共享资源:线程之间可以共享进程的地址空间和资源,这使得线程间的通信和数据共享变得更加简单高效。
  • 上下文切换:线程之间的上下文切换成本较低,因为它们共享相同的地址空间。

应用场景:线程通常用于实现并发执行和任务分解。例如,在一个图形编辑器中,可以使用多个线程分别处理用户输入、图形渲染和文件保存等任务,从而提高程序的响应速度和执行效率。

总结:进程是一个独立的程序执行实例,具有独立的地址空间和资源;线程是进程内的一个执行单元,共享进程的地址空间和资源。进程通常用于执行独立的、需要隔离的任务,而线程则用于实现并发执行和任务分解

  • 内部是多线程实现,主要负责页面渲染,脚本执行,事件处理,网络请求等

image.png

JS引擎 VS 渲染引擎

JS引擎和渲染引擎是浏览器中两个非常重要的组件。它们分别负责处理JavaScript代码和渲染网页内容

  1. 解析执行JS

    • JS引擎:JS引擎负责解析和执行JavaScript代码。它会将JavaScript源代码解析成抽象语法树(AST),然后通过JIT编译器将AST编译成机器代码,最后执行机器代码。在这个过程中,JS引擎还会进行优化,提高代码的执行效率。常见的JS引擎包括V8(用于Chrome和Node.js)、SpiderMonkey(用于Firefox)和JavaScriptCore(用于Safari)。
    • 渲染引擎:渲染引擎的主要任务是解析HTML和CSS,构建DOM树和渲染树,然后将渲染树转换为屏幕上的像素。在这个过程中,渲染引擎并不直接处理JavaScript代码。然而,当遇到需要执行的JavaScript代码时(例如,通过<script>标签或事件处理程序),渲染引擎会将控制权交给JS引擎。一旦JS引擎执行完毕,渲染引擎会继续执行后续的渲染任务。
  2. XML解析生成渲染树,显示在屏幕

    • JS引擎:JS引擎主要处理JavaScript代码,不负责解析XML或生成渲染树。然而,JavaScript可以通过DOM API操作DOM树,从而间接地影响渲染树的生成和更新。
    • 渲染引擎:渲染引擎负责解析HTML和XML文档,构建DOM树。然后,渲染引擎会解析CSS样式,并将样式信息附加到DOM树上,生成渲染树。接下来,渲染引擎会计算每个节点的布局信息(如位置和大小),并将渲染树转换为屏幕上的像素。这个过程被称为“绘制”或“渲染”。
  3. 桥接方式通信

    • JS引擎和渲染引擎之间需要进行通信,以确保JavaScript代码的执行和网页内容的渲染能够协同工作。这种通信通常通过浏览器内部的API和事件机制实现。
    • JS引擎:JS引擎提供了DOM API,使得JavaScript代码可以访问和操作DOM树。当JavaScript代码需要对DOM树进行操作时(如添加、删除或修改节点),JS引擎会通过浏览器内部的API通知渲染引擎进行相应的

image.png

渲染进程 - 多线程工作流程

  1. 网络线程负责加载网页资源

    • 网络线程的主要任务是从服务器加载网页资源,如HTML、CSS、JavaScript、图片等。加载过程中,网络线程会将HTML和CSS交给渲染线程进行解析,并将JavaScript交给JS引擎进行解析和执行
  2. JS引擎解析JS脚本并且执行

    • JS引擎将接收到的JavaScript代码解析成抽象语法树(AST),然后通过JIT编译器将AST编译成机器代码,最后执行机器代码。在这个过程中,JS引擎还会进行优化,提高代码的执行效率
  3. JS解析引擎空闲时,渲染线程立即工作

    • 当JS引擎空闲时(即没有JavaScript代码需要执行),渲染线程开始工作。渲染线程负责解析HTML和CSS,构建DOM树和渲染树,计算布局信息,并将渲染树转换为屏幕上的像素。如果在此过程中遇到JavaScript代码(如<script>标签或事件处理程序),渲染线程会暂停,将控制权交给JS引擎
  4. 用户交互、定时器操作等产生回调函数放入任务队列中

    • 当用户与网页进行交互(如点击、滚动等),或者有定时器触发(如setTimeoutsetInterval)时,相应的回调函数会被放入任务队列中。任务队列是一个先进先出(FIFO)的数据结构,用于存储待处理的任务
  5. 事件线程进行事件循环,将队列里的任务取出交给JS引擎执行

    • 事件线程负责管理事件循环。事件循环的主要任务是检查任务队列,将队列中的任务按顺序取出,并交给JS引擎执行。JS引擎执行任务时,可能会对DOM树进行操作,从而触发渲染线程的更新。事件循环会持续进行,直到任务队列为空,或者浏览器关闭。

image.png

八股文笔试题

这段代码主要用于演示setTimeout的执行顺序以及JavaScript的单线程执行特点

  1. 输出1:定时器1的回调函数在10ms后加入事件队列。然而,由于while循环会阻塞主线程,直到至少20ms过去,所以定时器1的回调函数将在while循环结束后立即执行。因此,输出1的值会大于等于20ms,例如:"time10 20"。
  2. 输出2:定时器2的回调函数在30ms后加入事件队列。因为while循环在20ms后就结束了,所以定时器2的回调函数会在预期的30ms后执行。因此,输出2的值会接近30ms,例如:"time30 30"。
  3. 输出3:这个输出表示while循环结束时已经过去的时间。由于while循环会一直执行,直到至少20ms过去,所以输出3的值会大于等于20ms,例如:"20"。

主要注意的是,由于JavaScript的单线程特性和事件循环机制,setTimeout并不能保证回调函数会在精确的延迟时间后执行。实际的执行时间可能会受到其他任务(如while循环)的影响。

// 获取当前时间戳
const now = Date.now();
​
// 定时器1:10ms后输出"time10"和定时器1的执行延时
setTimeout(() => {
  console.log('time10', Date.now() - now); // 输出1:?
}, 10);
​
// 定时器2:30ms后输出"time30"和定时器2的执行延时
setTimeout(() => {
  console.log('time30', Date.now() - now); // 输出2:?
}, 30);
​
// 一直执行循环,直到距离当前时间戳至少过去20ms
while (true) {
  if (Date.now() - now >= 20) {
    break;
  }
}
​
// 输出当前已经过去的时间(自now开始计算)
console.log(Date.now() - now); // 输出3:?
​
复制代码

Chrome运行原理

Chrome运行原理 - 如何展示网页

  • 浏览器地址输入URL后发生了什么(完整流程)
  1. 输入解析:浏览器首先解析输入的内容,判断它是一个有效的URL还是搜索查询。如果输入的内容被识别为搜索查询,浏览器将使用默认的搜索引擎进行搜索。
  2. DNS查询:如果输入的内容是一个有效的URL,浏览器将进行域名系统(DNS)查询以将域名解析为对应的IP地址。浏览器会首先检查本地DNS缓存,如果找不到对应的记录,它会向DNS服务器发送查询请求。
  3. 建立连接:浏览器与目标服务器建立一个TCP连接。这通常包括三次握手过程,以确保双方都准备好进行数据传输。
  4. 发送HTTP请求:TCP连接建立后,浏览器向服务器发送一个HTTP请求。请求通常包括请求方法(如GET或POST)、请求的资源路径、HTTP协议版本、请求头(包含诸如用户代理、接受的编码和语言等信息)以及可能的请求体(如POST请求所包含的表单数据)。
  5. 接收响应:服务器处理HTTP请求,并将响应数据发送回浏览器。响应通常包括HTTP状态码(如200表示成功,404表示未找到等)、响应头(包含诸如内容类型、内容长度等信息)以及响应体(通常是HTML文档)。
  6. 关闭或重用连接:一旦浏览器接收到完整的响应数据,它可以选择关闭TCP连接或将其保持在活动状态以用于后续请求。
  7. 解析HTML:浏览器解析HTML文档,构建DOM树。在解析过程中,浏览器可能遇到<script>标签或其他需要立即执行的脚本,这时浏览器将暂停解析,执行脚本,然后继续解析。
  8. 请求其他资源:HTML文档通常包含其他资源的引用,如CSS、JavaScript和图片等。浏览器将发送额外的HTTP请求来获取这些资源。这些请求可能与初始请求的服务器相同,也可能涉及其他服务器。
  9. 构建渲染树:浏览器解析CSS样式,并将其应用于DOM树,生成渲染树。渲染树包含了所有可见元素及其样式信息。
  10. 布局:浏览器计算渲染树中每个元素的位置和大小,生成布局信息。
  11. 绘制:浏览器根据渲染树和布局信息将元素绘制到屏幕上。这个过程称为绘制或渲染。浏览器会将各个层的元素绘制到位图中,然后将这些位图合成到屏幕上显示的最终图像。
  12. 交互:在完成页面绘制后,浏览器开始接收和处理用户的交互,如点击、滚动、输入等。这些交互可能会触发JavaScript事件处理程序,从而修改DOM或应用新的样式。这些修改可能会导致浏览器重新布局和绘制页面的部分或全部内容。
  13. 关闭或卸载:当用户导航到其他页面或关闭浏览器选项卡时,浏览器将触发相应的页面卸载事件,如beforeunloadunload。这给开发者一个机会来执行清理操作,如保存用户数据或取消挂起的网络请求。一旦完成这些操作,浏览器将卸载页面并释放相关资源。

总结:当您在浏览器地址栏输入URL后,浏览器会经历一系列的操作,从DNS查询和TCP连接建立,到HTTP请求、响应处理、HTML解析、资源加载、渲染树构建、布局、绘制以及用户交互等。这些操作共同构成了从输入URL到显示完整网页的整个过程

image.png

视频中的流程(属于浏览器主进程中的导航流程,目的是为了保证用户能够成功加载并显示所请求的网页):

  1. 输入处理:浏览器主进程会根据用户输入的URL进行解析和处理,包括检查URL的有效性、判断是否需要使用搜索引擎、判断是否需要使用HTTPS等等。在处理完输入之后,浏览器主进程将发起开始导航的请求。
  2. 开始导航:浏览器主进程会向网络进程发送开始导航的请求,以便进行后续的页面加载操作。在发送请求之前,浏览器主进程会先判断当前页面是否支持复用,如果支持,则会重用渲染进程,否则会创建一个新的渲染进程来加载页面。
  3. 读取响应:一旦网络进程开始加载页面,它会读取服务器响应的数据,并将其返回给浏览器主进程。在读取响应的过程中,网络进程会进行数据解码和安全检查等操作。
  4. 寻找渲染进程:一旦浏览器主进程收到网络进程返回的响应数据,它会根据响应数据中的渲染进程ID来寻找对应的渲染进程。如果已经存在对应的渲染进程,则将响应数据发送给该进程进行处理;否则,浏览器主进程会创建一个新的渲染进程来处理该页面。

输入处理

  1. 用户Url框输入内容的后,UI线程会判断输入的是一个URL地址呢,还是一个query查询条件
  2. 如果是URL,直接请求站点资源
  3. 如果是query,将输入发送给搜索引擎

image.png

开始导航

  1. 当用户按下回车,UI线程通知网络线程发起一个网络请求,来获取站点内容
  2. 请求过程中,tab处于loading(转圈等待)状态

image.png

读取响应

  1. 网络线程接收到HTTP响应后,先检查响应头的媒体类型(MIME Type)
  2. 如果响应主体是一个HTML文件,浏览器将内容交给渲染进程处理
  3. 如果拿到的是其他类型文件,比如Zip、exe等,则交给下载管理器处理

image.png

寻找渲染进程

  1. 网络线程做完所有检查后,会告知主进程数据已准备完毕,主进程确认后为这个站点寻找一个渲染进程
  2. 主进程通过IPC消息告知渲染进程去处理本次导航
  3. 渲染进程开始接收数据并告知主进程自己已开始处理,导航结束,进入文档加载阶段

image.png

渲染进程 - 资源加载

  1. 收到主进程的消息后,开始加载HTML文档
  2. 除此之外,还需要加载子资源,比如一些图片,CSS样式文件以及JavaScript脚本

image.png

渲染进程 - 构建渲染树

  1. 构建DOM树,将HTML文本转化成浏览器能够理解的结构
  2. 构建CSSOM树,浏览器同样不认识CSS,需要将CSS代码转化为可理解的CSSOM
  3. 构建渲染树,渲染树是DOM树和CSSOM树的结合

image.png

渲染进程 - 页面布局

  1. 根据渲染树计算每个节点的位置和大小
  2. 在浏览器页面区域绘制元素边框
  3. 遍历渲染树,将元素以盒模型的形式写入文档流

image.png

渲染进程 - 页面绘制

  1. 构建图层:为特定的节点生成专用图层
  2. 绘制图层:一个图层分成很多绘制指令,然后将这些指令按顺序组成一个绘制列表,交给合成线程(减少绘制次数)
  3. 合成线程接收指令生成图块
  4. 栅格线程将图块进行栅格化
  5. 展示在屏幕上

image.png

前端性能performance

performance对象中常用的属性和方法:

  1. navigationStart:浏览器开始导航到当前页面的时间戳。

  2. fetchStart:浏览器开始检索文档的时间戳。

  3. responseStart:浏览器收到第一个字节的时间戳。

  4. domLoading:浏览器开始解析文档的时间戳。

  5. domInteractive:浏览器完成解析文档的时间戳。

  6. domContentLoadedEventStart:文档DOMContentLoaded事件开始的时间戳。

  7. domComplete:文档解析完成的时间戳。

  8. loadEventStart:文档加载事件开始的时间戳。

  9. loadEventEnd:文档加载事件结束的时间戳。

  • 最常用的是navigationStartloadEventEndnavigationStart表示浏览器开始导航到当前页面的时间戳,loadEventEnd表示文档加载事件结束的时间戳。这两个时间戳之差就是页面的加载时间
  1. 时间都花在哪?

  2. 什么情况下卡顿

    卡顿一般是由于浏览器在渲染页面时遇到了阻塞或耗时操作导致的。以下是一些可能导致卡顿的情况:

    1. JavaScript执行时间过长:JavaScript的执行会阻塞页面的渲染。如果脚本执行时间过长,会导致页面无响应或卡顿现象。
    2. 大量的DOM操作:DOM操作会影响页面的渲染,特别是在大量DOM操作时。如果需要频繁地更新DOM元素,可以考虑使用虚拟DOM等技术来减少DOM操作次数。
    3. 大量的网络请求:浏览器在渲染页面时需要下载和解析HTML、CSS、JavaScript和图像等资源。如果有大量的资源需要下载,会导致页面加载时间过长。
    4. 大量的样式和布局计算:如果页面包含大量的样式和布局计算,会影响页面的渲染性能。
    5. 阻塞渲染的JavaScript:如果脚本阻塞了页面的渲染,就会导致卡顿或页面无响应

image.png

首屏优化