浏览器原理

date
Feb 3, 2022
slug
browser-core
status
Published
tags
原理
summary
作为一个前端写得语言都是在浏览器运行的,所以了解浏览器原理的帮忙会很大。
type
Post
宏观视角下的浏览器打开浏览器发生了什么?进程和线程是什么?为什么有时一个页面蹦其他页面也会蹦?为什么要放在一个渲染进程中呢?TCP/IP 协议浏览器可以同时打开多个页签,他们端口一样吗?如果一样,数据怎么知道去哪个页签?TCP传送数据时 浏览器端就做渲染处理了么?如果前面数据包丢了 后面数据包先来是要等么?类似的那种实时渲染怎么处理?针对数据包的顺序性?http 和 websocket都是属于应用层的协议吗?关于 "数据在传输的过程中有可能会丢失或者出错",丢失的数据包去哪里了?凭空消失了吗?出错的数据包又变成啥了? 为什么会出错?即使是如今的多进程架构,我偶尔还会碰到一些由于单个页面卡死最终崩溃导致所有页面崩溃的情况,请问这是什么原因呢?您提到浏览器主进程负责将渲染进程生成的图片显示在ui上面,就是说渲染进程输出的最终是图片,浏览器显示的是图片,那么为什么浏览器中鼠标能选中文字?如果页面是图片的话文字是选不中的啊,这里面的机制又是怎样的?为什么单进程浏览器当时不可以采用安全沙箱?JS 执行机制JS 是按顺序执行的吗?JS 会出现栈溢出吗?为什么引入 let 、const?代码中相同变量,JS 引擎如何选择?JS 中的 this 是怎么回事?V8 工作原理数据是如何储存的?垃圾数据是如何自动回收的?v8 如何执行 JS 代码的?浏览器中的页面循环系统页面是如何 ”活“ 起来的?setTimeout 是如何实现的?XMLHttpRequest 是如何实现的?所有任务都是一个待遇吗?什么是 Promise?什么是 async/await?浏览器中的网络HTTP1 协议HTTP2 协议HTTP2 其他特性依然存在的优化HTTP3QUIC 协议HTTP3 的挑战浏览器安全为什么 XMLHttpRequest 不能跨域?为什么 Cookie 中有 HttpOnly 属性?为什么陌生链接不要随便点?为什么页面和系统之间要隔离?如何让数据传输更安全?浏览器如何验证数字证书?

宏观视角下的浏览器

打开浏览器发生了什么?

启动浏览器就会启动 5 个进程,分别是GPU、网络、储存、音频、备用渲染进程,
这些进程是每个页面都需要的基础服务,
而每打开一个的页签都是启动了一个新的渲染进程,
通过排版引擎(HTML & CSS)和 V8 引擎(JS)转换为可以交互的网页。
注:Google Chrome:95.0.4638.69 (正式版本) (arm64)

进程和线程是什么?

  • 进程内可以用多个线程,线程可以并发执行或串行
  • 进程就是程序的 运行实例
  • 进程是线程宿主,线程不可独立存在
  • 进程内的线程之间 数据共享
  • 进程之间内容相互隔离,IPC 可实现进程间
 

为什么有时一个页面蹦其他页面也会蹦?

当在一个页面打开的新页面符合同一站点,那么他们将被分配到一个渲染进程里面,
这样会导致当某一页面崩溃是,共享渲染进程内的全部页面都会崩溃。

为什么要放在一个渲染进程中呢?

因为在一个渲染进程里面,他们就会共享JS的执行环境,也就是说A页面可以直接在B页面中执行脚本。
 

TCP/IP 协议

  • IP: 将数据包送达目的 主机
  • UDP:把数据 快速 的送达应用程序
    • 数据不保证完整,一般应该与互动游戏、直播等领域
  • TCP:把数据 完整 的送达应用程序
    • 重传机制、排序机制
notion image
  • http/1.1: 一个 tcp 同时只能处理一个请求,浏览器会为每个域名维护 6 个 tcp 连接,但每个 tcp 可以复用
  • http2:可 并行请求 资源的,但浏览器只会为每个域名维护 一个 tcp 连接
 
关于浏览器文件缓存类型的说明
  • disk:新 tab 打开时,都是disk;html 文件都是 disk
  • memory:新 tab 再次使用缓存时,是 memory
 

浏览器可以同时打开多个页签,他们端口一样吗?如果一样,数据怎么知道去哪个页签?

端口一样的,网络进程知道每个tcp链接所对应的标签是那个,所以接收到数据后,会把数据分发给对应的渲染进程。

TCP传送数据时 浏览器端就做渲染处理了么?如果前面数据包丢了 后面数据包先来是要等么?类似的那种实时渲染怎么处理?针对数据包的顺序性?

接收到http响应头中的content-type类型时就开始准备渲染进程了,响应体数据一旦接受到便开始做DOM解析了!基于http不用担心数据包丢失的问题,因为丢包和重传都是在tcp层解决的。http能保证数据按照顺序接收的(也就是说,从tcp到http的数据就已经是完整的了,即便是实时渲染,如果发生丢包也得在重传后才能开始渲染)

http 和 websocket都是属于应用层的协议吗?

都是应用层协议,而且websocket名字取的比较有迷惑性,其实和socket完全不一样,可以把websocket看出是http的改造版本,增加了服务器向客户端主动发送消息的能力。

关于 "数据在传输的过程中有可能会丢失或者出错",丢失的数据包去哪里了?凭空消失了吗?出错的数据包又变成啥了? 为什么会出错?

比如网络波动,物理线路故障,设备故障,恶意程序拦截,网络阻塞等等
 

即使是如今的多进程架构,我偶尔还会碰到一些由于单个页面卡死最终崩溃导致所有页面崩溃的情况,请问这是什么原因呢?

 
是这样的,通常情况下是一个页面使用一个进程,但是,有一种情况,叫"同一站点(same-site)",具体地讲,我们将“同一站点”定义为根域名(例如,geekbang.org)加上协议(例如,https:// 或者http://),还包含了该根域名下的所有子域名和不同的端口,比如下面这三个:https://time.geekbang.orghttps://www.geekbang.orghttps://www.geekbang.org:8080都是属于同一站点,因为它们的协议都是https,而根域名也都是geekbang.org。
你也许了解同源策略,但是同一站点和同源策略还是存在一些不同地方,在这里你需要了解它们不是同一件事就行了。
Chrome的默认策略是,每个标签对应一个渲染进程。
但是如果从一个页面打开了新页面,而新页面和当前页面属于同一站点时,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫process-per-site-instance。
直白的讲,就是如果几个页面符合同一站点,那么他们将被分配到 一个渲染进程 里面去。
所以,这种情况下,一个页面崩溃了,会导致同一站点的页面同时崩溃,因为他们使用了同一个渲染进程。为什么要让他们跑在一个进程里面呢?因为在一个渲染进程里面,他们就会共享JS的执行环境,也就是说A页面可以直接在B页面中执行脚本。因为是同一家的站点,所以是有这个需求的。
 

您提到浏览器主进程负责将渲染进程生成的图片显示在ui上面,就是说渲染进程输出的最终是图片,浏览器显示的是图片,那么为什么浏览器中鼠标能选中文字?如果页面是图片的话文字是选不中的啊,这里面的机制又是怎样的?

 
点击鼠标选中文字的时候,这些消息会传递到渲染进程,渲染进程再合成选中文字的状态,然后更新图

为什么单进程浏览器当时不可以采用安全沙箱?

如果一个进程使用了安全沙箱之后,该进程对于操作系统的权限就会受到限制,比如不能对一些位置的文件进行读写操作,而这些权限浏览器主进程所需要的,所以安全沙箱是不能应用到浏览器主进程之上的。
 

JS 执行机制

JS 是按顺序执行的吗?

JS 会出现栈溢出吗?

为什么引入 let 、const?

代码中相同变量,JS 引擎如何选择?

JS 中的 this 是怎么回事?

 

V8 工作原理

数据是如何储存的?

垃圾数据是如何自动回收的?

v8 如何执行 JS 代码的?

 

浏览器中的页面循环系统

页面是如何 ”活“ 起来的?

setTimeout 是如何实现的?

XMLHttpRequest 是如何实现的?

所有任务都是一个待遇吗?

什么是 Promise?

什么是 async/await?

 

浏览器中的网络

HTTP 协议 是浏览器和服务器之间的 通信语言

HTTP1 协议

  • HTTP/0.9: 图灵完备,但功能单一
    • 需求:主要用于学术交流,用来在网络之间 传递 HTML 超文本 的内容
    • 特点:没有 HTTP 请求头和请求体,服务器也没有返回头信息
  • HTTP/1.0:面向大众,解决刚需
    • 需求:支持多种类型的文件下载
      • 通过请求头和响应头来进行协商文件的类型、压缩方式、编码等
    • 特点:
      • 新增 状态码:过响应行的方式来通知浏览器的
      • 新增 Cache:缓存已经下载过的数据
      • 新增 用户代理:收集客户端基础信息
    • 缺点:
      • 每一次 HTTP 通信都要经历:TCP连接 → 传输 HTTP 数据 → 断开 TCP
      • 相当于每次和别人说一句话都要 say hello 一次,非常的搞笑
  • HTTP/1.1: 缝缝补补
    • 核心优化项:
      • 持久连接
        • 只要服务器或浏览器没有明确断开连接,TCP 连接会一直保持
        • 默认开启,同一个域名最多同时建立 6 个 TCP 连接
      • 使用 CDN 实现 域名分片 机制
        • 将一个页面的资源利用多个域名下载,提高 TCP 并发数量
      • 不成熟的 HTTP 管线化
        • 持久连接虽能减少 TCP 重连次数,但 TCP 的连接还是串行的,如果有请求没有及时返回就会阻塞后面的请求,著名的 队头阻塞 问题
        • 管线化是要解决队头阻塞的问题,但该方案由于种种原因被放弃
      • 提供 虚拟机 的支持
        • HTTP/1.0 中,每个域名绑定唯一 IP 地址,一个 IP 地址只能对应一个物理机,但随着虚拟机技术的发展,一个物理机可以有多个虚拟机,每个虚拟机都有自己的单独域名,但域名的 IP 是一样的
        • 因此,新增了 Host 字段,来表示当前域名,服务器就可以根据 Host 做不同的处理
      • 支持 动态生成内容
        • HTTP/1.0 中,用 Content-Length 来确定数据的大小,但是随着服务端的发展,很多页面内容都是 动态生成 的,所以在传输之前不知道最终的数据大小,导致浏览器 无法确定 数据是否已全部接收
        • Chunk transfer 机制:服务器会将数据分割成若干 任意大小 数据块且附加其长度,最后使用 零长度 的数据块作为发送完成标志
      • 客户端 Cookie
        • 解决 HTTP 无状态的限制,用于 标识 用户身份和信息
    • 依然存在的问题:
      • 带宽的利用率却并不理想
        • 带宽:每秒最大能发送或接收的字节数
          • 上行带宽:每秒能 发送 的最大字节数
          • 下行带宽:每秒能 接收 的最大字节数
        • 原因:
            1. TCP 的 慢启动
                • TCP 建立连接之后就进入发送数据状态,发送数据的速度 由慢到快,慢启动是 TCP 为了减少 网络拥塞 的一种缓冲策略
                • 页面中常用的一些关键资源文件本来就不大,如 HTML/CSS/JS 文件,通常这些文件在 TCP 建立连接后就要发起请求的,但由于是慢启动,耗时比正常时间要多很多,延迟了首次渲染页面的时长
            1. 资源竞争
                • 多条 TCP 连接会竞争固定带宽时无法区分重要资源的 优先级
                • 当页面中下载的资源较多且带宽不够时,TCP 连接就会动态减慢接收数据的速度,而这个减慢没有优先级,也就会造成视频和图片这种较大的资源会和 HTML /CSS 这种渲染初始页面的 资源竞争 而造成白屏
            1. 队头堵塞
                • 每个 TCP 管道中同一时刻只能处理一个请求,请求未结束时,其他请求只能等待,类似于在同一个窗口排队办业务

HTTP2 协议

解决 HTTP1.1 中 TCP 的慢启动、带宽资源竞争、队头拥塞问题,虽然 TCP 是造成这些问题的源头,但是目前依然无法脱离 TCP 协议,只能想办法规避上述问题。
  • 如何解决队头堵塞?
    • 多路复用机制
      • 浏览器每个发起的请求都会携带一个 ID,当服务器返回数据时也会携带对应的 ID,浏览器会将返回的 ID 的内容拼接为完整的 HTTP 响应数据
        • 就好比去一个可以自助点餐的饭店,点完之后就等着上菜,解决了排队点菜造成的对头堵塞的问题
      • 可将请求分成一帧一帧的数据进行传输,当收到优先级高的请求时,服务器可以暂停之前的请求来 处理优先级较高 的资源请求
      • 实现机制:
        • HTTP2 新增了 二进制分帧层
          1. 浏览器准备好 请求数据
          1. 二进制分帧层将请求数据 转换 为一个个带有请求 ID 编号的帧,通过协议栈将其发送给服务器
          1. 服务器接收所有帧,会将所有 相同 ID 的帧 合并为一条完整的请求信息
          1. 服务器处理该请求,并将处理的响应行、响应头、响应体 分别发送 至二进制分帧层
          1. 二进制分帧层会将响应数据转换为一个个带有请求 ID 编号的帧,经过协议栈发送给浏览器
          1. 浏览器接收到响应帧后,会根据 ID 编号将帧的数据提交给对应的请求
          flowchart BT
          	subgraph HTTP2 协议栈
          	  subgraph 请求列表
          	      ...
              end
          		subgraph 二进制分帧层
          	    subgraph 完整请求
          				请求头+ID
          				响应头+ID
          				响应体+ID
          			end
              end
          		请求列表 <-->	二进制分帧层 <--> TLS[TLS 可选] <--> 		TCP/IP
          end
         
  • 如何解决 TCP 慢启动次数和 TCP 之间宽带资源竞争?
    • 一个域名只使用一个 TCP 长连接 来传输数据,减少每次 TCP 连接造成的 副作用

HTTP2 其他特性

  1. 可设置请求的优先级:优先加载重要资源
  1. 服务器推送
    1. 当刚请求 HTML 后,直接将 HTML 所需的 CSS/JS 也一块发送给浏览器,将于拿来这对首次打开页面的速度起来了 至关重要 的作用。
  1. 头部压缩

依然存在的优化

  • TCP 传输层 的队头阻塞 :
    • 虽然 HTTP2 解决了 应用层面到传输层 的队头阻塞问题,但是 传输层到网络层 的丢包重传机制还是可以堵塞 TCP 管道,而 TCP 最初就是为了单连接而设计的
    • 在丢包严重的情况下 HTTP2 比 HTTP1.1 表现还差,因为后者有 6 个 TCP 管道
  • TCP 建立连接的延时:
    • 在传输数据之前,需要花费 TCP + TLS 握手所消耗的 3~4 RTT
  • TCP 协议僵化:
    • 因 TCP 发展到现在的历史包袱已经太重,而又属于计算机相对的底层和核心的位置,全设备同步是不可能的
 

HTTP3

甩掉 TCP、TLS 的包袱,构建高效网络

QUIC 协议

在考虑兼容中间设备的僵化,重新构建一个新的 传输层协议 并被很好兼容,已无可能,只能在现有基础上做拓展和优化。
因为 HTTP3 选择折中的方案 — UDP 协议,基于 UDP 实现了类似于 TCP 的功能,这套协议称作为 QUIC 协议。
HTTP/2 和 HTTP/3 协议栈
HTTP/2 和 HTTP/3 协议栈

HTTP3 的挑战

  1. 兼容性:服务器和浏览器都没有对 HTTP3 提供比较完整的支持
  1. UDP 的市场成熟度:系统内核对 UDP 的优化远远没有达到 TCP 的优化程度
  1. 中间设备僵化:其对 UDP 的优化程度低于 TCP,使用 QUIC协议时, 3%~7% 的丢包率
 
总结:从 HTTP 0.9 到未来的 HTTP3.0,协议的变化也是随着时代和市场的需求而变化,每个大版本的变化都是为了解决一个核心问题,而每一次的更改也都需要慎重,当不可逆的行为发生时,会形成沉重的历史包袱,甚至会延迟社会的进步,直到被忍无可忍时将其推翻,到时为此买单的可能将是整个世界。
 

浏览器安全

为什么 XMLHttpRequest 不能跨域?

为什么 Cookie 中有 HttpOnly 属性?

为什么陌生链接不要随便点?

为什么页面和系统之间要隔离?

如何让数据传输更安全?

浏览器如何验证数字证书?


© jianxiaoBai 2021 - 2022