1.1Chrome架构——多进程
概念:进程与线程
线程不是单独存在的,他由进程来启动和管理。
进程就是一个程序运行的实例。
当启动一个程序的时候,操作系统就会为程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个环境叫做进程。
进程与线程的特点:
- 进程中的任意一线程执行出错,都会导致整个进程崩溃
- 线程之间共享进程中的数据
- 当进程关闭了之后,操作系统会回收进程所占用的内存(被泄漏的内存也会回收)
- 进程之间的内容相互隔离(但是会有进程通信IPC机智)
单进程浏览器时代
单进程浏览器时代,浏览器所有的功能模块都在一个进程内,包括网络、插件、JavaScript运行环境、渲染引擎和页面等模块。
但是那么多模块都在一个进程内,容易导致浏览器不稳定、不流畅、不安全。
不稳定
由于全部模块都在一个进程内,如果一个线程出现问题,那么整个进程就无法继续运行。
而早期浏览器通过插件来实现Web视频、游戏等功能,而插件也很容易出问题,所以浏览器就容易奔溃,不稳定。同时当js代码出现问题话(比如一个死循环),整个浏览器也会奔溃。
不流畅
从上面可以看到,页面线程包括了很多模块,这也就意味着,同时只能有一个模块在运行着,如果一个模块占用时间太长或者死循环,就会造成浏览器的卡顿。
页面的内存泄漏也容易造成浏览器变慢,当运行了一个比较复杂的页面再关闭页面,会存在内存不能完全回收的情况;这也就导致了使用时间越长,内存占用越高,浏览器会变得越来越慢。
不安全
从插件和脚本两个角度:
插件:插件可以通过操作系统获取到电脑的任意资源
脚本:脚本可以通过浏览器的漏洞来获取系统权限,同样会引发安全问题。
多进程浏览器时代
早期多进程架构:此时页面有多个进程,分别负责不同的内容。
解决不稳定的问题
由于各个模块是在不同进程里运行的,所以一个进程出错的时候,影响到的仅仅是当前的页面进程或者插件进程,并不会影响到浏览器的其他页面。
解决不流畅的问题
由于js运行在渲染进程里,所以当js代码有死循环的时候,影响到的也只是当前页面,其他页面不会出现问题。
而对于内存泄露问题,由于关闭一个页面,就关闭了他的整个渲染进程,所以能完整的回收对应的内存。
解决安全问题
由于设置了安全沙箱,相当于操作系统给进程上了一把锁,进程可以运行,但是不能修改硬盘里面的任何数据,也不能读取敏感数据,这也就保障了安全问题。
目前的多进程架构
一个浏览器主进程 + 一个网络进程 + 一个GPU进程 + 多个渲染进程 + 多个插件进程
浏览器进程:负责页面的显示,用户交互,子进程管理等
网络进程:主要负责页面网络资源的加载。
GPU进程:用于CSS、3D效果等页面的绘制。
渲染进程:核心任务是将HTML、CSS、JS转化为用户与之交互的界面。它运行在沙箱模式下,并且为每个tab标签创建一个渲染进程。
插件进程:主要负责插件的运行,因为插件容易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
带来的一些问题
- 更高的资源占用:因为每个进程都会包含公共基础结构的副本
- 更复杂的体系结构:各模块之间耦合性高,扩展性差
未来面向服务的架构
原来的各个模块会被重构成独立的服务,每个服务都可以在独立的进程中运行,访问服务必须使用定义好的接口,通过IPC来通信。
\
1.2HTTP请求流程
浏览器发起HTTP请求
1、构建请求
2、查找缓存
发起请求之后,浏览器回去浏览器缓存中查询是否有要请求的文件。(缓存是在本地保存文件资源副本)
如果发现有请求的资源的副本,则直接拦截请求,并返回该资源的副本;然后结束本次请求。
好处:
- 减少请求服务器的次数,减轻服务器的压力
- 实现资源的快速加载
如果缓存内没有该资源的副本,则就会进入网络请求的环节。
3、准备ip地址和端口
在HTTP开始工作之前,浏览器需要通过TCP与服务器建立联系。
而建立联系的第一步就是要准备ip地址以及端口。
ip地址通过DNS域名系统来获取,端口没有指定的话就默认8080
4、等待TCP队列
由于同一个域名下最多只能建立6个TCP链接,多出的请求就会变成等待状态。
5、建立TCP链接
通过三次握手建立TCP链接
6、发送HTTP请求
请求包括了请求行、请求头、请求体
假如使用POST方法,用于发送一些数据给服务器,这时候这些数据就会放在请求体。
请求头还包括有cookie、浏览器内核等信息
服务器处理HTTP请求
1、返回请求
状态码代表请求是否成功
响应头包含了一些服务器自身的信息和cookie等
响应体通常包含HTML的实际内容
2、断开连接
通常一旦服务器向客户端返回了信息,就要关闭TCP连接。
但是假如浏览器或者服务器在其头信息中加入了Connection:Keep-Alive,那么TCP连接在发送后依旧保持打开状态,这样就可以省去下一个建立连接的时间。
3、重定向
有时候输入baidu.com最终打开的页面是www.baidu.com
这是因为第一次请求baidu.com的时候,服务器响应的状态时301,返回了重定向的地址,然后会重新对这个地址发送请求。
很多站点第二次打开速度会很快
浏览器缓存机制:
关键属性:
- Cache-Control(设置缓存时间)
- If-None-Match(检查资源是否有更新)
- Not Modified (资源没有更新,可以继续使用环迅)
登录状态如何保持
- 服务器会将用户身份的字符串放在响应头的
Set-Cookie字段里面 - 浏览器解析响应头的时候,会将字段信息保存到本地
- 用户再次访问的时候,浏览器发起请求之前,会将字段写进请求头的cookie字段里面
- 服务器收到cookie字段会进行校验用户信息
总结
发起一个http请求时,浏览器首先会构建请求,然后检查本地是否有缓存资源,若有则直接读取;若没有,则发送请求。首先准备ip地址以及端口,然后等待TCP队列;轮到了之后建立TCP的连接,连接成功之后发起HTTP请求,服务器进行处理,服务器返回请求,接收到响应之后,TCP连接断开(不一定)
1.3导航流程 -从输入URL到页面展示
总过程
- 浏览器进程:主要负责与用户的交互、子进程管理等
- 网络进程:面向渲染进程和浏览器进程等提供网络下载功能
- 渲染进程:将从网络下载下来的HTML、JavaScript等资源解析成可以显示和交互的页面。同时渲染进程是在安全沙箱里的。
整个过程:
- 用户输入请求
- 网络进程发起请求
- 服务器响应请求后,开始准备渲染进程
- 准备好后,向渲染进程提交页面数据,接受到数据后进行渲染
用户输入
用户在地址栏中输入一个关键字后,会判断是搜索内容还是URL
- URL:会将其加工成完整的URL(例如带上http)
- 关键字:会加工成带搜索关键字的URL
URL请求
过程相当于上一节的HTTP请求流程,请求服务器并获取响应数据
返回的数据有一个
Content-Type字段,用来区分是HTML页面还是下载类型等(text/html、application/octet-stream)
准备渲染过程
- 通常情况下,打开新的页面都会使用单独的渲染进程
- 但如果是同一站点的话,会使用相同的渲染进程(同一站点:同一协议与根域名)
此时渲染进程准备好了之后,不能立即解析文档,因为文档还在网络进程中,需要提交文档到渲染进程中。
提交文档
- ‘提交文档’的消息是由浏览器进程发出的,渲染进程收到该消息之后,会与网络进程建立传输数据的管道
- 数据传输完成之后,渲染进程会提交”确认提交“的消息给浏览器进程
- 浏览器进程收到”确认提交“之后,会更新浏览器界面的状态,包括安全状态,地址栏URL,web页面(暂时空白)、前进后退的历史记录(此时还没有到渲染阶段)
渲染阶段
渲染阶段具体在后面两个小节,渲染结束之后,渲染进程会发消息给浏览器进程,接收到消息之后,就会停止标签图标上的加载动画。
1.4渲染流程
大致流程
- 构建DOM树
- 样式计算
- 布局阶段
- 分层
- 绘制
- 分块
- 光栅化
- 合成
构建DOM树
因为浏览器无法直接理解HTML语言,所以要构建成他能理解的DOM结构。(HTML和DOM树内容几乎一样,只是DOM是保存在内存中的树状结构)
样式计算
-
将CSS转化为stylesheets:浏览器不能识别CSS,所以要转化成为他能识别的stylesheets
-
转换样式表中的属性值,使其标准化
- 计算出每个DOM树中节点的具体样式:通过CSS的继承与层叠,计算出DOM节点的具体样式
布局阶段
有了DOM树和样式,还不足以显示页面,因为还不知道DOM节点的几何位置,所以把计算节点的几何位置叫做布局。
创建布局树
这个过程会忽略所有不会显示的节点,只加入页面会显示的节点
布局计算
进行了布局计算之后,会将计算结果加入到布局树中,所以这个阶段,输入也是布局树,输出也是布局树,这也是不合理的一个地方。
分层——生成图层树
为了更方便的实现渲染效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一颗对应的图层树
哪些节点可以创建新的层:
-
拥有层叠上下文属性的元素会被提升为单独一层
-
需要裁剪的地方会创建为图层
图层绘制
在完成图层树的构建之后,渲染引擎会对图层树的每个图层进行绘制。会把每个层的绘制拆分成很多个小的绘制指令,形成绘制列表。
栅格化操作
上诉操作只是生成了绘制列表,实际上绘制操作是由渲染引擎里面的合成线程来完成的,上述是在主线程完成的。
-
合成线程会将图层划分为图块
-
然后栅格化线程池会将图块生成为位图
-
栅格化的过程中还会使用GPU进程的GPU来加速生成(这一步跨进程了)
合成和显示
所有图块被栅格化了之后,合成线程就会生成绘制图块的命令,提交给浏览器进程,然后对应的组件就会进行绘制,并且显示在屏幕上。
总结
- 生成DOM树
- 计算样式
- 计算布局
- 分层生成图层树
- 生成绘制列表(将列表提交至合成线程)
- 划分成图块
- 将图块栅格化成位块(借助栅格化线程池,以及GPU进程)
- 浏览器对应组件生成页面(浏览器进程)