深入浅出Electron:原理、工程与实践
上QQ阅读APP看书,第一时间看更新

2.1 Chromium原理

我们知道Electron内置了Chromium(https://www.chromium.org/)和Node.js(https://nodejs.org/),而且Electron的实现方式受这两个项目的影响很大,所以在剖析Electron原理前,有必要先简单讲解一下Chromium和Node.js这两个项目的原理。

我们不可能在一个小节内把Chromium的原理讲完,所以这里截取Chromium的多进程架构这一个特点来聊Chromium的原理,我认为这是对Electron影响最大的一个架构特点。

首先读者应该对浏览器引擎有一个基本的认识:构建一个永远不会崩溃的浏览器引擎几乎是不可能的,但几十年来浏览器的开发工程师都在朝着这个方向努力。

在2006年左右,任何一个浏览器产品都很容易崩溃,因为它们的架构设计模式就像过去的单用户、多任务协同操作系统一样。在这样的操作系统中,应用程序的不当行为,比如没有及时释放资源或陷入了死循环操作,都可能会使整个系统崩溃。

同样,浏览器中任何一个行为不当的网页也可能如此。一个页面或插件的错误就可能让整个浏览器崩溃。

现代操作系统更加健壮,因为它们将应用程序置于彼此隔离的独立进程中。一个应用程序的崩溃通常不会损害其他应用程序或操作系统。

Chromium也做了类似的设计,它把每个页面约束在单独的进程中,以保护整个浏览器不受单个页面中的故障影响。它甚至还限制了每个页面进程对其他进程和系统其他部分的访问,这极大地缓解了浏览器容易崩溃的问题。

Chromium把管理页面、管理选项卡和插件的进程称为主进程。把特定于页面的进程称为渲染进程。

渲染进程使用Blink开源布局引擎来解释和渲染HTML。渲染进程与主进程通过IPC管道进行通信。

每个渲染进程都对应一个全局的RenderProcess对象,都有一个或多个RenderView对象。RenderProcess对象负责与主进程通信并管理这些RenderView对象,通常每个新窗口或选项卡都会在新进程中打开。主进程负责创建这些新的进程,并指示它们创建各自的RenderView对象。

主进程负责监视追踪这些渲染进程,一旦渲染进程崩溃或挂起,则主进程控制着界面,提示用户需要重新加载页面。当用户点击“重新加载”按钮后,主进程则创建一个新的渲染进程来为用户服务。

有时候需要在选项卡或窗口之间共享渲染进程。比如开发者使用window.open方法打开新窗口时,就希望这个窗口复用当前的渲染进程,因为两个窗口之间往往需要同步的数据交互。

另外还有一些情况需要复用渲染进程,比如打开一个新的渲染进程时,发现系统中已经有一个同样的渲染进程可以复用的情况。Electron也提供了类似的API供开发者复用渲染进程。

由于渲染进程运行在一个单独的进程中,所有页面脚本都在此进程中运行,当页面脚本尝试访问网络或本地资源时,当前渲染进程并不提供这样的服务,而是发消息给主进程,由主进程完成相应的工作,此时主进程会判断这些操作是否合法,比如跨越同源策略的请求、突破限制访问Cookie、https页面内内嵌http的页面等,这些行为都是不合法的行为,主进程可以拒绝提供服务,这就是浏览器的沙箱模式。

多进程模式还带来了性能上的提升,对于那些不可见的渲染进程,操作系统会在用户可用内存较低时,把它们占用内存的部分或全部交换到磁盘上,以保证用户可见的进程更具响应性。

相比之下,单进程浏览器架构将所有页面的数据随机分布在内存中,不可能如此干净地分离使用和未使用的数据,性能表现不佳。

也正是因为这些优势,以及对更高安全性的追求,Chromium团队在分离进程的道路上越走越远,举个例子,比如一个页面内包含多个不同域下的iframe,这些iframe也会被分离到不同的渲染进程中。

多进程模式并不是没有缺点,比如每个进程都会包含公共基础结构的副本(例如V8引擎的执行环境)、更复杂的通信模型等,这都意味着浏览器会消耗更多的内存、CPU甚至电能。

虽然Chromium团队在这方面做了很多优化工作,但由于底层架构所限,优化工作并没有太大的成效(这也是为什么Electron应用资源消耗较大的底层原因)。Chromium多进程架构图如图2-1所示。

029-1

图2-1 Chromium多进程架构图(来源:Chromium官网)

Electron也继承了Chromium的多进程模式,每个BrowserWindow、BrowserView、WebView都对应一个渲染进程(并不是很准确,但基本可以这么理解),进程间通信也是通过IPC管道来实现的。多进程模式带来的缺点在Electron应用中也都存在。

可以这么说:没有Chromium就没有V8(Chromium内置的高性能JavaScript执行引擎),没有V8就没有Node.js。Chromium的高性能并不单单是多进程架构的功劳,V8引擎也居功甚伟,V8引擎以超高性能执行JavaScript脚本著称,Node.js的作者也是因为这一点才决定封装V8,把JavaScript程序员的战场引向客户端和服务端。下面就简单介绍一下Node.js的原理。