【浏览器工作原理】01 - 宏观视角下的浏览器

250 阅读12分钟

前言

前端是以浏览器为基石开展工作的,所以我们可以从浏览器来完善我们的知识体系,首先要知道的是浏览器的五个核心部分(同时也是前端的 5 个核心部分):

  1. 浏览器架构设计
  2. JavaScript 引擎工作原理
  3. 页面工作原理 - (事件循环系统,构建页面的核心流程)
  4. 浏览器网络
  5. 浏览器安全

首先对于浏览的运行机制,我们要有一个大致的了解,比如浏览器是如何来管理各个应用的

线程和进程

  • 线程 - 线程不是单独存在的,它是由进程来启动和管理的。
  • 进程 - 一个进程是一个应用程序的运行实例,启动一个程序的时候,操作系统会为这个程序创建一块内存,用来存放代码、运行中的数据、和一个执行任务的主线程,这样的运行环境叫做进程

image.png

图中可以知道,线程是依附于进程的,而进程中使用多线程并行处理可以提高运算效率

总的来说,进程和线程的关系有以下几个特点:

  1. 进程中任一线程执行出错,都会导致整个进程的奔溃
  2. 线程之间共享进程中的数据
  3. 当一个进程关闭之后,操作系统会回收进程所占用的内存(如果任一线程因为操作不对导致内存泄漏,当进程退出时,内存也会被回收)
  4. 进程之间的内容相互隔离

浏览器架构设计

最新的Chrome浏览器包括:

  • 1个浏览器主进程 - 主要负责页面显示、用户交互、子进程管理和存储功能
  • 1个GPU进程 - GPU 用来绘制图形,比如3D CSS
  • 1个网络进程 - 主要负责网页的网络资源加载
  • 多个渲染进程 - 核心任务是将HTML、CSS、JavaScript转换为可以和用户交互的网页,排版引擎BLink和JavaScript引擎V8都是运行在该进程中的,默认情况下,Chrome会为每个Tab表情创建一个渲染进程,处于安全考虑,渲染进程都是运行在沙箱模式下。
  • 多个插件进程 - 由于插件容易奔溃,所以需要独立的进程来隔离,以此保证插件奔溃不会导致浏览器奔溃

那么到这里有个问题:

现在的多进程架构还是会出现单个页面奔溃导致多个页面奔溃的情况,为什么?

通常情况是一个页面一个进程,但是有一种情况叫做同一站点(same-site):我们将同一站点定义为根域名加上协议,还包含了根域名下所有子域名和不同的端口,比如: time.geekbang.org、 www.geekbang.orgwww.geekbang.org:8080 都是属于同一站点(和同源策略是有区别的,不是一个东西)

Chrome的默认策略是,每个标签对应一个渲染进程,如果从一个页面打开了一个新页面,而新页面和当前页面属于同一站点,那么新页面会复用父页面的渲染进程,官方把这个默认策略叫做:process-pre-site-instance

所以如果几个页面符合同一站点,那么他们将会被分配到同一个渲染进程里面去,所以这就回答了题目的问题

那么问题又来了,为什么他们要放在同一个渲染进程里,因为这样的话,他们就会共享JS的执行环境,也就是A页面可以直接在B页面中执行脚本(有这种需求)

如何保证页面文件能被完整送达浏览器

衡量 Web 页面的时候有一个重要指标 - FP (First Paint)从页面加载到首次开始绘制的时长

这个指标直接意味着更低的用户跳出率,更快的页面,意味着更多的 PV,以及更多的转化率,那什么影响 FP 指标呢?答案就是 网络加载速度

这个部分有两个重要协议:TCP/IP 和 HTTP

TCP / IP

在 Web 中 TCP/IP 是如何工作的呢?我们可以从发送一个数据包来了解这个问题:

1. IP:把数据包传送到目的主机

数据包在互联网上进行传输,就要符合网际协议(IP)标准,互联网上的不同在线设备都有唯一的地址,和家庭收件地址类似,只要知道地址,我们就可以收发包裹。

计算机的地址叫做 IP 地址,访问任何网站实际上只是你的计算机向另一台计算机请求信息

所以我们如果想要把一个数据包从主机 A 发送给主机 B,那么在传输之前数据包就会被附加上主机 B 的 IP 地址信息,这样在传输的过程中才能正确的寻址,

额外的数据包还会附加上主机 A 本身的 IP 信息,有了这些信息主机 B 才能回复信息给 A,这些附加的信息会被装进一个叫做 IP 头的数据结构中,IP 头是 IP 数据包的套不信息,包含:IP 版本、源 IP 地址、目标 IP 地址、生存时间等信息。

所以到这里我们就能知道一个数据包从主机 A 到主机 B 的过程:

  • 上层将含有 '具体信息' 的数据包交给网络层
  • 网络层将 IP 头附加在数据包上,组成新的 IP 数据包,交给底层
  • 底层通过物理网络将数据包传输给主机 B
  • 数据包被传输到主机 B 的网络层,在这里主机 B 拆开数据包的 IP 头信息,将拆出来的数据部分交给上层
  • 这样,含有 '具体信息' 的数据包就到达了主机 B 的上层

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

IP 协议是非常底层的协议,只负责把数据包传送到对方电脑,但是对方电脑并不知道交给哪个程序,因此需要基于 IP 之上开发可以和应用打交道的协议:

最常见的就是用户数据包协议(User Datagram Protocol)简称 - UDP

UDP 中的一个最重要的信息是端口号,端口号其实是一个数字,每个需要访问网络的程序都要绑定一个端口号,通多端口号 UDP 就可以把指定的数据包发送给指定程序了。

所以:IP 协议通过 IP 地址信息把数据包发送给指定的主机,而 UDP 通过端口号把数据包发给正确的程序。 和 IP 头一样,端口号会被附加在 UDP 头部信息里,UDP 头再和原始数据包组合成新的 UDP 数据包。UDP 头中除了目的端口,当然还有源端口等信息。

有了 UDP 的加入,我们就能更加细化一下数据包从主机 A 到主机 B 的过程:

  • 上层将含有 '具体信息' 的数据包交给网络层
  • 传输层会在数据包前面附加上 UDP 头,组成新的 UDP 数据包,再将新的 UDP 数据包交给网络层
  • 网络层将 IP 头附加在数据包上,组成新的 IP 数据包,交给底层
  • 数据包被传输到主机 B 的网络层,在这里主机 B 拆开数据包的 IP 头信息,将拆出来的数据部分交给传输层
  • 在传输层中,数据包中的 UDP 头会被拆开,并根据 UDP 中提供的端口号,把数据部分交给上层应用程序
  • 这样,含有 '具体信息' 的数据包就到达了主机 B 的上层应用程序中

3. TCP:把数据完整的送达到应用程序(传输层)

对于浏览器请求,对于邮件这种要求数据传输可靠性的应用,如果使用 UDP 来传输会有两个问题:

  • 数据包在传输过程中容易丢包
  • 大文件会被拆成很多小的数据包来传输,这些小数据会经过不同的路由,并且在不同的时间到达接收端,而 UDP 协议并不知道如何组装这些数据包,从而把它们还原成完整的文件 所以对于这种问题引入了 TCP (Transmission Control Protocol)协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议, 相对于 UDP,TCP 有下面的两个特点:
  1. 对于数据包的丢失情况,TCP 提供重传机制
  2. TCP 引入了数据包排序机制,用来保证乱序的数据包可以组合成一个完整的文件,和 UDP 一样,TCP 除了包含目标端口和本机端口外,还提供了关于排序的序列号,以便接收端通过序号来重排数据包。 所以我们可以来看下完整的 TCP 连接过程:

image.png

从上图我们可以看到,一个完整的 TCP 连接的生命周期包括:三次握手建立连接、传输数据、四次挥手断开连接 这三个阶段

  • 三次握手建立连接

  • 传输数据

  • 四次挥手断开连接

HTTP

HTTP 协议是建立在 TCP 连接基础之上的,HTTP 是一种允许浏览器像服务器获取资源的协议,是 Web 的基础

了解 HTTP 请求,我们可以从回答一个经典问题来了解:

当我们在浏览器输入一个 URL 那么接下来浏览器会完成哪些动作?

  1. 构建请求

  2. 查找缓存

    • 在发起真正的网络请求之前,浏览器会先在浏览器缓存中查询是否有要请求的文件,其中,浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术。
    • 当浏览器发现请求的资源已经在浏览器缓存中存有副本,他会拦截请求,返回资源的副本,并且直接结束请求,而不会再去源服务器重新下载,这样做的好处是:
      • 缓解服务器端压力,提升性能(获取资源的耗时更短)
      • 对于网站来说,缓存是实现快速资源加载的重要组成部分
    • 如果缓存查找失败,进入下一步网络请求
  3. 准备 IP 地址和端口

    因为浏览器使用 HTTP 协议作为应用层协议,用来封装请求的文本信息,并且使用 TCP/IP 作为传输层协议将它发送到网络上

    所以在 HTTP 开始工作之前,浏览器需要通过 TCP 与服务器建立连接,也就是说 HTTP 的内容是通过 TCP 的传输数据阶段来实现的

    HTTP 网络请求的第一步是和服务器建立 TCP 连接,而建立连接需要的信息,需要准备的 IP 和端口号是怎么来获取的呢? 由于我们在地址栏输入了 URL,可以得到是从 URL 来获取 IP 和端口信息的,这就是另一个服务 DNS的由来 DNS - 负责把域名和 IP 地址做一一映射关系,这套域名映射为 IP 的系统叫做域名系统(Domain Name System)

    这样我们就可以推导出来,首先第一步浏览器会请求 DNS 返回域名对应的 IP(浏览器当然还提供了 DNS 数据缓存服务),如果某个域名已经解析过了,就直接返回缓存的解析结果,以供下次查询使用(减少请求) 拿到 IP 之后接下来就是获取端口号,通常情况下如果 URL 没有特别指明端口,那么 HTTP 协议默认是 80 端口。

  4. 等待 TCP 队列

    Chrome 有个机制,同一个域名同时最多之能建立 6 个 TCP 连接,如果在同一个域名下面同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成

    当然,如果请求数量少于 6,会直接进入下一步建立 TCP连接

  5. 建立 TCP 协议

  6. 发送 HTTP 请求

    首先浏览器会向服务器发送请求行,包括了 请求方法、请求 URL、和 HTTP 版本协议,发送请求行的作用就是告诉浏览器需要什么资源,比如 GET、POST

    在浏览器发送请求行命令之后,还要以请求头的形式发送一些其他的信息,把一些浏览器的基础信息告诉服务器,比如包含了浏览器所使用的的操作系统,浏览器内核、当前请求的域名信息、浏览器的 cookie 信息等等。

  7. 服务器处理 HTTP 请求,返回请求

    首先服务器会返回响应行,包括协议版本和状态码,发送完响应头后服务器就可以继续发送响应体的数据,通常响应体就包含了 HTML 的实际内容

  8. 断开连接

通常情况下,一旦服务器向客户端返回了请求数据,它就要关闭 TCP 连接。不过如果浏览器或者服务器在其头信息中加入了:Connection:Keep-Alive 那么 TCP 连接在发送后将仍然保持打开状态,这样浏览器就可以继续通过同一个 TCP 连接发送请求。保持 TCP 连接可以省去下次请求时需要建立连接的时间,提升资源加载速度。比如,一个 Web 页面中内嵌的图片就都来自同一个 Web 站点,如果初始化了一个持久连接,你就可以复用该连接,以请求其他资源,而不需要重新再建立新的 TCP 连接。

  1. 重定向 当我们访问一个网站如果它的初始返回状态码是 301 的话,那么就说明我们需要重定向到另一个网站,而需要重定向的网址正是包含在响应头的 Location 字段中,接下来,浏览器获取 Location 字段中的地址,并使用该地址重新导航,这就是一个完整重定向的执行流程。这也就解释了为什么输入的是 A 网址,最终打开的却是 B 网址 了。

总结

下层为上层提供服务,TCP 为 HTTP 提供查错校验,超时重传机制