大家好,又见面了,我是你们的朋友全栈君。
文章目录
- 现代浏览器原理揭秘
- 前提计算机基础概念
- 1.CPU(Central Processing Unit)
- 2.GPU(Graphics Processing Unit)
- 3.进程 & 线程
- 一、浏览器架构
- 多进程的好处
- 多进程的坏处
- 多进程架构的内存优化
- 网站隔离 (Site Isolation)
- 二、一个经典问题, 导航时都发生了什么?
- 1. 处理输入
- 2.开始导航
- 3.读取相应
- 4.寻找一个渲染进程来绘制⻚面
- 5.提交导航
- 6.加载完成
- 三、导航到不同的站点
- 1.如果第二次导航是在⻚面内发起的, 比如⻚面内Js执行了location.href=xxxx, 这时候浏览器是怎么做的?
- 2.如果第二次导航是到不同的站点呢?
- 四、Service Worker场景下的导航
- 五、导航预加载 – Navigation Preload
- 参考文章
- 前提计算机基础概念
现代浏览器原理揭秘
首先我们先了解一些关键的计算机术语以及Chrome浏览器的多进程架构。
前提计算机基础概念
1.CPU(Central Processing Unit)
中央处理器, 解释计算机指令以及处理计算机软件中的数据, 功能强大,它可以串行地一件接着一件处理交给它的任务
现代电脑上cpu通常会有多个核心, 比如经常听到的8核处理器, 4核处理器等等。因为多核心CPU可以大大提高手机和电脑的运算能力
CPU的核心数是指物理上,也就是硬件上存在着几个核心。比如,双核就是包括2个相对独立的CPU核心单元组,四核就包含4个相对独立的CPU核心单元组
2.GPU(Graphics Processing Unit)
图形处理器, 单个GPU核心只能处理一些简单的任务,不过它胜在数量多,单片GPU上会有很多很多的核心可以同时工作,也就是说它的并行计算能力是非常强的
当你在手机或者电脑上打开某个应用程序的时候,背后其实是CPU和GPU支撑着这个应用程序的运行。通常来说,你的应用要通过操作系统提供的一些机制才能跑在CPU和GPU上面。
3.进程 & 线程
进程 – 可以看成正在被执行的应用程序(executing program)。
线程 – 是跑在进程里面的,一个进程里面可能有一个或者多个线程,这些线程可以执行任何一部分应用程序的代码
当你启动一个应用程序的时候,操作系统会为这个程序创建一个进程同时还为这个进程分配一片私有的内存空间,这片空间会被用来存储所有程序相关的数据和状态。
当你关闭这个程序的时候,这个程序对应的进程也会随之消失,进程对应的内存空间也会被操作系统释放掉。
很多应用程序都会采取多进程的方式来工作,因为进程和进程之间是互相独立的,它们互不影响。
换句话来说,如果其中一个工作进程挂掉了,其他进程不会受到影响,而且挂掉的进程还可以重启
一、浏览器架构
上图所示,Chrome浏览器, 是多进程架构, 先来看一下都包含哪些进程
- Browser(一个) – 浏览器进程, 只有一个浏览器进程,负责浏览器的主体部分,包括导航栏,书签, 前进和后退按钮, 提供存储等功能
- Network(一个) – 网络进程, 主要负责⻚面的网络资源加载,之前是作为一个模块运行在浏览器进程里 面的,直至最近才独立出来,成为一个单独的进程
- GPU(一个) – 图像渲染进程, 负责独立于其它进程的GPU任务。它之所以被独立为一个进程是因为它 要处理来自于不同tab的渲染请求并把它在同一个界面上画出来。
- Renderer(多个) – 渲染进程, 负责tab内和网⻚展示相关的所有工作, 比如将 HTML、CSS 和 JavaScript 转换为用戶可以与之交互的网⻚, 默认情况下每个tab都有一个独立的渲染进程。
- Plugin(多个) – 插件进程,例如之前的flash
- Extensions(多个) – 扩展程序进程
- 其他进程 – 工具进程,辅助框架等等
可以在chrome浏览器 更多工具 – 任务管理器 查看当前浏览器所开启的进程, 以及内存和cpu消耗
多进程的好处
- 容错性 Chrome会为每个tab单独分配一个属于它们的渲染进程(render process)。 举个例子,假如你有三个tab,你就会有三个独立的渲染进程。 当其中一个tab的崩溃时,你可以随时关闭这个tab并且其他tab不受到影响。可是如果所有的tab都跑在同一个进程的话,它们就会有连带关系,一个挂全部挂。
- 安全性和沙盒性
因为操作系统可以提供方法让你限制每个进程拥有的能力,所以浏览器可以让某些进程不具备某些特定的功能。 例如,由于tab渲染进程可能会处理来自用戶的随机输入,所以Chrome限制了它们对系统文件随机读写的能力。
- 每个进程可以拥有更多内存 因为每个进程都会分配一块独立的内存空间, 所以理所当然的, 每个进程都会有更多的内存。
多进程的坏处
其实上面已经提到了, 每个进程都会拥有自己独立的内存空间, 他们并不能像同一个进程中的线程一 样共享内存空间。
而一些基础的东西比如V8 Javascript引擎, 会在不同进程的内存空间中同时存在, 所以就消耗了不必 要的内存。
多进程架构的内存优化
- 那么Chrome是怎么优化这种情况的呢? 答案就是:限制启动的进程数目,当进程数目达到一定界限后, Chrome会将访问同一个网站的tab都放在一个进程里面跑。
- Chrome的服务化, 节省更多的内存
同样的优化方法也可以被使用在浏览器进程(browser process)上面。
Chrome浏览器的架构正在发生一些改变,目的是将和浏览器本身(Chrome)相关的部分拆分为一 个个不同的服务,服务化之后,这些功能既可以放在不同的进程里面运行也可以合并为一个单独的 进程运行。
这样做的主要原因是让Chrome在不同性能的硬件上有不同的表现。当Chrome运行在一些性能比较 好的硬件时,浏览器进程相关的服务会被放在不同的进程运行以提高系统的稳定性。相反如果硬件 性能不好,这些服务就会被放在同一个进程里面执行来减少内存的占用。
这样,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进 程中运行,访问服务(Service)必须使用定义好的接口,通过 IPC 来通信,从而构建一个更内聚、 松耦合、易于维护和扩展的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。
网站隔离 (Site Isolation)
网站隔离会为网站内不同站点的iframe分配一个独立的渲染进程。
之前说过Chrome会为每个tab分配一个单独的渲染进程,可是如果一个tab只有一个进程的话不同站 点的iframe都会跑在这个进程里面,这也意味着它们会共享内存,这就有可能会破坏同源策略。
同源策略是浏览器最核心的安全模型,它可以禁止网站在未经同意的情况下去获取另外一个站点的 数据,因此绕过同源策略是很多安全攻击的主要目的。
而进程隔离(proces isolation)是隔离网站最好最有效的办法了。因此在Chrome 67版本之后,桌面 版的Chrome会默认开启网站隔离功能,这样每一个跨站点的iframe都会拥有一个独立的渲染进程。
二、一个经典问题, 导航时都发生了什么?
上面提到过,浏览器中tab外面发生的一切都是由浏览器进程(browser process)控制的。
浏览器进程有很多负责不同工作的线程(worker thread),其中包括:
- UI线程(UI thread):绘制浏览器顶部按钮和导航栏输入框等组件,当你在导航栏里面输入一个 URL的时候,其实就是UI线程在处理你的输入。
- 存储线程(storage thread): 控制文件读写。
所以,当你在导航栏上输入一串内容的时候,Chrome到底为我们做了哪些工作?
1. 处理输入
当用戶开始在导航栏上面输入内容的时候,UI线程(UI thread)做的第一件事就是询问:“你输入的 字符串是一些搜索的关键词(search query)还是一个URL地址呢?”。
因为对于Chrome浏览器来说,导航栏的输入既可能是一个可以直接请求的域名,也可能是用戶想在 搜索引擎里面搜索的关键词信息, 所以当用戶在导航栏输入信息的时候,UI线程要进行一系列的解析来判定是将用戶输入发送给搜索 引擎还是直接请求你输入的站点资源。
2.开始导航
当用戶按下回⻋键的时候,UI线程会通知网络进程初始化一个网络请求来获取站点的内容。 这时候tab上的icon会展示一个提示资源正在加载中的旋转圈圈,而且网络进程会进行一系列诸如
DNS寻址以及为请求建立TLS连接的操作。
- tips: 这时如果网络进程收到服务器的HTTP 301重定向响应,它就会告知UI线程进行重定向然后它会再次发起一个新的网络请求。
3.读取相应
- 响应类型判断
网络进程在收到HTTP响应的主体时,在必要的情况下它会先检查一下流的前几个字节以确定响应主体的具体媒体类型(MIME Type)。 响应主体的媒体类型一般可以通过HTTP头部的Content-Type来确定,不过Content-Type有时候会缺失或者是错误的,这种情况下浏览器就要进行MIME类型嗅探来确定响应类型了。 这里可以打开一个窗口, 看一下Content-type响应头
- 不同响应类型的处理
如果响应的主体是一个HTML文件,浏览器会将获取的响应数据交给渲染进程(renderer process) 来进行下一步的工作。如果拿到的响应数据是一个压缩文件(zip file)或者其他类型的文件,响应数据就会交给下载管理器 (download manager)来处理。
- 安全检查
网络进程在把内容交给渲染进程之前还会对内容做SafeBrowsing检查。
如果请求的域名或者响应的内容和某个已知的病毒网站相匹配,网络进程会给用戶展示一个警告的⻚面。除此之外,网络进程还会做CORB(Cross Origin Read Blocking)检查来确定那些敏感的跨站数据不会被发送至渲染进程。
4.寻找一个渲染进程来绘制⻚面
在网络进程做完所有的检查后并且能够确定浏览器应该导航到该请求的站点,它就会告诉UI线程所 有的数据都已经被准备好了。
UI线程在收到网络进程的确认后会为这个网站寻找一个渲染进程(renderer process)来渲染界面。
- tips. 这里chrome有个小优化
因为网络请求的耗时可能会很⻓, 所以第二步中当UI线程发送URL链接给网络进程后,它其实已经知 晓它们要被导航到哪个站点了。
所以在网络进程干活的时候,UI线程会主动地为这个网络请求启动一个渲染线程。如果一切顺利的话(没有重定向之类的东西出现),网络进程准备好数据后⻚面的渲染进程已经就准备好了,这就 节省了新建渲染进程的时间。
不过如果发生诸如网站被重定向到不同站点的情况,刚刚那个渲染进程就不能被使用了,它会被摒弃,一个新的渲染进程会被启动。
5.提交导航
到这一步的时候,数据和渲染进程都已经准备好了,浏览器进程(browser process)会通过IPC告诉渲染进程去提交本次导航(commit navigation)。
除此之外浏览器进程还会将刚刚接收到的响应数据流传递给对应的渲染进程让它继续接收到来的HTML数据。
一旦浏览器进程收到渲染线程的回复说导航已经被提交了(commit),导航这个过程就结束了,文档的加载阶段(document loading phase)会正式开始。
到了这个时候,导航栏会被更新,安全指示符和站点设置会展示新⻚面相关的站点信息。 当前tab的会话历史(session history)也会被更新,这样当你点击浏览器的前进和后退按钮也可以 导航到刚刚导航完的⻚面。为了方便你在关闭了tab或窗口(window)的时候还可以恢复当前tab和 会话(session)内容,当前的会话历史会被保存在磁盘上面。
6.加载完成
当导航提交完成后,渲染进程开始着手加载资源以及渲染⻚面。
一旦渲染进程完成渲染(load),它会通过IPC告知浏览器进程,然后UI线程就会停止导航栏上的loading
三、导航到不同的站点
上面讲述了一个导航的过程, 那么这时候如果我们想去浏览另一个网⻚, 浏览器会怎么做呢?
能够想到的是, 浏览器必然会重复一遍导航的步骤, 但是在这之前, 浏览器还有一些收尾工作要做!
浏览器进程会对渲染进程说, 我准备重新发起导航了, 你那边是否需要处理**beforeunload**事件?
beforeunload可以在用戶重新导航或者关闭当前tab时给用戶展示一个“你确定要离开当前⻚面吗?”的二次确认弹框。
浏览器进程之所以要在重新导航的时候和当前渲染进程确认的原因是,当前⻚面发生的一切(包括 ⻚面的JavaScript执行)是不受它控制而是受渲染进程控制,它不知道里面的具体情况。
- tips:所以不要随便给⻚面添加beforeunload事件监听,你定义的监听函数会在⻚面被重新导航的时候执行,因此这会增加重导航的时延。 **beforeunload**事件监听函数只有在十分必要的时候才能被添加,例如用戶在⻚面上输入了数据, 并且这些数据会随着⻚面消失而消失。
1.如果第二次导航是在⻚面内发起的, 比如⻚面内Js执行了location.href=xxxx, 这时候浏览器是怎么做的?
渲染进程会自己先检查一个它有没有注册beforeunload事件的监听函数,如果有的话就执行,执行完后发生的事情就和之前的情况没什么区别了,唯一的不同就是这次的导航请求是由渲染进程给浏览器进程发起的。
2.如果第二次导航是到不同的站点呢?
会有另外一个渲染进程被启动来完成这次重导航,而当前的渲染进程会继续处理现在⻚面的一些收尾工作,例如unload事件的监听函数执行。
四、Service Worker场景下的导航
如果开发者在service worker里设置了当前的⻚面内容从缓存里面获取,当前⻚面的渲染就不需要重
新发送网络请求了,这就大大加快了整个导航的过程。 这里要重点留意的是service worker其实只是一些跑在渲染进程里面的JavaScript代码。
那么问题来了,当导航开始的时候,浏览器进程是如何判断要导航的站点存不存在对应的service worker并启动一个渲染进程去执行它的呢?
其实service worker在注册的时候,它的作用范围(scope)会被记录下来。
在导航开始的时候,网络进程会根据请求的域名在已经注册的service worker作用范围里面寻找有没 有对应的service worker。如果有命中该URL的service worker,UI线程就会为这个service worker启 动一个渲染进程(renderer process)来执行它的代码。Service worker既可能使用之前缓存的数据 也可能发起新的网络请求。
五、导航预加载 – Navigation Preload
在上面的例子中,你应该可以感受到如果启动的service worker最后还是决定发送网络请求的话,浏 览器进程和渲染进程这一来一回的通信包括service worker启动的时间其实增加了⻚面导航的时延。
导航预加载就是一种通过在service worker启动的时候并行加载对应资源的方式来加快整个导航过程 效率的技术。预加载资源的请求头会有一些特殊的标志来让服务器决定是发送全新的内容给客戶端 还是只发送更新了的数据给客戶端。
参考文章
https://developers.google.com/web/updates/2018/09/inside-browser-part1
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/192923.html原文链接:https://javaforall.cn