浏览器工作原理与实践

2022/4/10

# 现代浏览器进程

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

  1. 浏览器主进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
  2. 渲染进程。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
  3. GPU 进程。其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
  4. 网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
  5. 插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

# 在浏览器中输入 URL 后发生了什么

  1. 用户输入 URL,浏览器会根据用户输入的信息判断是搜索还是网址,如果是搜索内容,就将搜索内容+默认搜索引擎合成新的 URL;如果用户输入的内容符合 URL 规则,浏览器就会根据 URL 协议,在这段内容上加上协议合成合法的 URL。
  2. 用户输入完内容,按下回车键,浏览器导航栏显示 loading 状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获得。
  3. 浏览器进程浏览器构建请求行信息,会通过进程间通信(IPC)将 URL 请求发送给网络进程 GET /index.html HTTP1.1
  4. 网络进程获取到 URL,先去本地缓存中查找是否有缓存文件,如果有,则去检查缓存的有效期。在有效期内的缓存资源直接使用,称之为强缓存,拦截请求,直接 200 返回。通常来说,刷新页面会使用内存缓存,关闭后重新打开会使用磁盘缓存。超过有效期的,则携带缓存的资源标识向服务端发起请求,校验是否能继续使用,如果服务端告诉我们,可以继续使用本地存储,则返回 304,并且不携带数据;如果服务端告诉我们需要用更新的资源,则返回 200,并且携带更新后的资源和资源标识缓存到本地,方便下一次使用;否则,进入网络请求过程。
  5. 网络进程请求 DNS 返回域名对应的 IP 和端口号,如果之前 DNS 数据缓存服务缓存过当前域名信息,就会直接返回缓存信息;否则,发起请求获取根据域名解析出来的 IP 和端口号,如果没有端口号,http 默认 80,https 默认 443。如果是 https 请求,还需要建立 TLS 连接、协商加密密钥。
  6. Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。如果当前请求数量少于 6 个,会直接建立 TCP 连接。
  7. TCP 三次握手建立连接,http 请求加上 TCP 头部——包括源端口号、目的程序端口号和用于校验数据完整性的序号,向下传输。请求的基本组成是请求行+请求头+请求体,请求的基本组成是请求行+请求头+请求体。
  8. 网络层在数据包上加上 IP 头部——包括源 IP 地址和目的 IP 地址,继续向下传输到底层。
  9. 底层通过物理网络传输给目的服务器主机。
  10. 目的服务器主机网络层接收到数据包,解析出 IP 头部,识别出数据部分,将解开的数据包向上传输到传输层。
  11. 目的服务器主机传输层获取到数据包,解析出 TCP 头部,识别端口,将解开的数据包向上传输到应用层。
  12. 应用层 HTTP 解析请求头和请求体,如果需要重定向,HTTP 直接返回 HTTP 响应数据的状态 code 301 或者 302,同时在请求头的 Location 字段中附上重定向地址,浏览器会根据 code 和 Location 进行重定向操作;如果不是重定向,首先服务器会根据 请求头中的 If-None-Match 的值来判断请求的资源是否被更新,如果没有更新,就返回 304 状态码,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了;否则,返回新数据,200 的状态码,并且如果想要浏览器缓存数据的话,就在相应头中加入字段:Cache-Control:Max-age=2000 响应数据又顺着应用层——传输层——网络层——网络层——传输层——应用层的顺序返回到网络进程。
  13. 数据传输完成,TCP 四次挥手断开连接。如果,浏览器或者服务器在 HTTP 头部加上如下信息,TCP 就一直保持连接。保持 TCP 连接可以省下下次需要建立连接的时间,提示资源加载速度 Connection:Keep-Alive
  14. 网络进程将获取到的数据包进行解析,根据响应头中的 Content-type 来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器,该导航流程结束,不再进行;如果是 text/html 类型,就通知浏览器进程获取到文档准备渲染。
  15. 浏览器进程获取到通知,根据当前页面 B 是否是从页面 A 打开的并且和页面 A 是否是同一个站点(根域名和协议一样就被认为是同一个站点),如果满足上述条件,就复用之前网页的进程,否则,新创建一个单独的渲染进程。
  16. 浏览器会发出“提交文档”的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的“管道”,文档数据传输完成后,渲染进程会返回“确认提交”的消息给浏览器进程。
  17. 浏览器收到“确认提交”的消息后,会更新浏览器的页面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 web 页面,此时的 web 页面是空白页。
  18. 渲染进程对文档进行页面解析和子资源加载,HTML 通过 HTM 解析器转成 DOM Tree(二叉树类似结构的东西),CSS 按照 CSS 规则和 CSS 解释器转成 CSSOM TREE,两个 tree 结合,形成 render tree(不包含 HTML 的具体元素和元素要画的具体位置),通过 Layout 可以计算出每个元素具体的宽高颜色位置,结合起来,开始绘制,最后显示在屏幕中新页面显示出来。
    1. 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。document
    2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。document.styleSheets
    3. 创建布局树,并计算元素的布局信息。
    4. 对布局树进行分层,并生成分层树。
    5. 为每个图层生成绘制列表,并将其提交到合成线程。
    6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
    7. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
    8. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
  19. 具有明确定位属性的元素、定义透明属性的元素、使用 CSS 滤镜 filter 的元素、设置了 z-index 的元素等,都拥有层叠上下文属性。
  • CSS 资源异步下载,下载和解析都不会阻塞构建 dom 树,可以放在 head 里
  • JS 资源同步下载,下载和执行都会阻塞构建 dom 树,所以要加 defer、async 或者放在 body 底部,使得渲染能尽早开始。
  • 在 JS 线程执行时,GUI 渲染线程没有办法去解析 HTML,这是因为 JS 可以操作 DOM,如果两者同时进行可能引起冲突。如果这时 JS 去修改了样式,那此时 CSS 的解析和 JS 的执行也没法同时进行了,会先等 CSS 解析完成,再去执行 JS,最后再去解析 HTML。从这个角度来看,CSS 有可能阻塞 HTML 的解析。

# DNS 查询过程

  1. 比如你有一个网站要上线,你在域名注册商那里申请了 abc.com。
  2. 那么你的域名 A 记录就保存在这个域名注册商的 DNS 服务器上,该 DNS 服务器称为权威域名服务器。
  3. 当客户端访问 abc.com 时,先查找浏览器 DNS 缓存,没有则查找操作系统 DNS 缓存,在这一阶段是操作系统 dnscache clinet 服务进行 DNS 缓存的(你在任务管理器里面可以看到一个 dns 客户端进程,就是这玩意实现缓存的)。
  4. 如果还是没有则查找/etc/hosts 文件中的域名记录。
  5. 然后依然没有的话则访问电脑上设置的 DNS 服务器 IP,比如三大营运商的 dns 服务器或者谷歌的 8.8.8.8,此时这一层的 DNS 服务器称为“野生 DNS 缓存服务器”,也就是非权威域名服务器。
  6. 如果还是没有则非权威域名服务器会去查找:根域名服务器->顶级域名服务器->二级域名服务器->权威域名服务器...,这样客户端就在权威域名服务器上找到了 abc.com 对应的 IP 了,这个 IP 可以是多个,每次客户端请求的时候域名服务器会根据负载均衡算法分配一个 IP 给你。
  7. 当 DNS 缓存失效了,则重新开始新一轮的域名请求。

总结如下:

  • 浏览器缓存->操作系统 dnscache ->hosts 文件->非权威域名服务器->根域名服务器->顶级域名服务器->二级域名服务器->权威域名服务器。
    • 其中非权威域名服务器还包括 LDNS(企业内网 DNS 服务器),三大营运商 DNS,谷歌公开的 DNS,微软公开的 DNS 等。
  • 别忘了互联网上还有另外一个重要的角色 CDN,它也会在 DNS 的解析过程中“插上一脚”。DNS 解析可能会给出 CDN 服务器的 IP 地址,这样你拿到的就会是 CDN 服务器而不是目标网站的实际地址。
  • 另外 DNS 请求有两种方式:递归查询和迭代查询,这方面大家可以网上了解一下。LDNS 往后面查询一般是递归查询,因为公司内网是有防火墙的,全部请求通过 LDNS 来递归查询然后把结果给内网用户。
  • DNS 缓存比较简单,它主要就是在浏览器本地把对应的 IP 和域名关联起来。

# chrome 打开其他应用的链接样例

vscode://vscode.github-authentication/did-authenticate?windowid=4&code=54d033a63ff757ffec89&state=2874c601-0a26-4a06-a763-74213002e571

上次更新: 7/13/2022