[译] Node.js 架构概览

译者按:
在 Medium 上看到这篇文章,行文头绪清晰,论述简明爽利,果断点下翻译按钮。
第一小节背景铺陈略烦琐,可以略过。刚最先我给这部份留了个 blah blah blah 直接翻背面的,翻完以后回头看,斟酌完整性才把第一节给补上。接下来的内容干货满满,置信对 Node.js 运行机制有兴致的读者一定会有所收成。

原文:Architecture of Node.js’ Internal Codebase
作者:Aren Li

起首,说点儿 JavaScript……

StackOverflow 的团结创始人 Jeff Atwood 在他有名的编程博客 Coding Horror 上说:

any application that can be written in JavaScript, will eventually be written in JavaScript.
任何可以用 JavaScript 写就的运用顺序,终究都邑以 JavaScript 写出来。

JavaScrit 的边境和影响力在过去几年里迅猛发展,如今已是最盛行的编程言语之一。2016 年爆栈网的开发者观察中,JavaScript 在最盛行手艺最热点问答两项排名第一,其他方面也首屈一指。

Node.js 是一个效劳器端 JavaScript 实行环境,供应了底层效劳器功用环境,包括二进制数据操纵、文件体系 I/O、数据库接见、收集接见等。它举世无双的特征使其在现存的多种成熟效劳器言语中脱颖而出,而且经过了业界抢先的科技公司如 Paypal、Tinder、Medium(是的,本文原文的谁人博客体系)、LinkedIn 和 Netflex 的实战运用,以至这些都发作在 Node.js 宣布 1.0 之前。

我最近在 StackOverflow 上回复一个关于 Node.js 内部代码构造的题目,因此而萌生了写作本文的动机。

Node.js 的官方文档实在讲得并不清晰它是什么:

一个基于 Chrome V8 引擎的 JavaScript 运行时。Node.js 采纳事宜驱动、非壅塞 I/O 模子……

要明白这段话和它背地的真正气力,我们须要把 Node.js 拆分到组件,相识它们的关键手艺,怎样交互合作,终究构成了 Node.js 这个壮大的运行时环境:

《[译] Node.js 架构概览》

组件和第三方依靠

V8:Google 开源的高机能 JavaScript 引擎,以 C++ 完成。这也是集成在 Chrome 中的 JS 引擎。V8 将你写的 JavaScript 代码编译为机器码(所以它超等快)然后实行。V8 有多快?看看这个爆栈网的回复

libuv:供应异步功用的 C 库。它在运行时担任一个事宜轮回(Event Loop)、一个线程池、文件体系 I/O、DNS 相干和收集 I/O,以及一些其他重要功用。

其他 C/C++ 组件和库:如 c-arescrypto (OpenSSL)http-parser 以及 zlib。这些依靠供应了对体系底层功用的接见,包括收集、紧缩、加密等。

运用/模块(Application/Modules):这部份就是一切的 JavaScript 代码:你的运用顺序、Node.js 中心模块、任何 npm install 的模块,以及你写的一切模块代码。你消费的重要精神都在这部份。

绑定(Bindings):Node.js 用了这么多 C/C++ 的代码和库,简朴来讲,它们机能很好。不过,JavaScript 代码末了是怎样跟这些 C/C++ 代码相互挪用的呢?这不是三种差别的言语吗?确切云云,而且一般差别言语写出来的代码也不能相互沟通,没有 binding 就不可。Binding 是一些胶水代码,可以把差别言语绑定在一起使其可以相互沟通。在 Node.js 中,binding 所做的就是把 Node.js 那些用 C/C++ 写的库接口暴露给 JS 环境。这么做的目标之一是代码重用:这些功用已有现存的成熟完成,没必要只是由于换个言语环境就重写一遍,假如桥接挪用一下就充足的话。另一个原因是机能:C/C++ 如许的体系编程言语一般都比其他高阶言语(Python、JavaScript、Ruby 等等)机能更高,所以把重要斲丧 CPU 的操纵以 C/C++ 代码来实行越发明智。

C/C++ Addons:Binding 仅桥接 Node.js 中心库的一些依靠,zlib、OpenSSL、c-ares、http-parser 等。假如你想在运用顺序中包括其他第三方或许你本身的 C/C++ 库的话,须要本身完成这部份胶水代码。你写的这部份胶水代码就称为 Addon。可以把 Binding 和 Addon 视为衔接 JavaScript 代码和 C/C++ 代码的桥梁。

术语

I/O:输入/输出(Input/Output)的缩写,基础上代指那些重要由计算机 I/O 子体系处置惩罚的操纵。重 I/O 操纵(I/O-bound operations)一般会牵涉到磁盘或驱动器接见,比方数据库接见或文件体系相干操纵。相似的观点另有重 CPU 操纵(CPU-bound)、重内存操纵(Memory-bound)等等。它们的辨别是依据体系哪部份机能对这个操纵有最大的影响。比方关于某项操纵而言,CPU 运算才进步可以带来最大的提拔,这项操纵就属于重 CPU 操纵。

非壅塞/异步:当一项请求发来,运用顺序会处置惩罚这个请求,其他操纵须要等这个请求处置惩罚完成才实行。这个流程的题目是:当大批请求并发时每一个请求都须要守候前一个完成,也就是说每一个请求都邑壅塞背面的一切请求,最蹩脚的是假如前一个请求花了很长时刻(比方从数据库读取 3GB 的数据)背面一切请求都随着悲剧了。解决方法可所以引入多处置惩罚器和(或)多线程架构,这些方法各有好坏。Node.js 采纳了另一种体式格局,不再为每一个请求开启一个新的线程,而是一切请求都在单一的主线程中处置惩罚,也只做这么一件事变:处置惩罚请求——请求中包括的 I/O 操纵如文件体系接见、数据库读写等,都邑转发给由 libuv 治理的事情线程去实行。也就是说,请求中的 I/O 操纵是异步处置惩罚的,而非在主线程上举行。这个方法就使得主线程从不会壅塞,由于一切耗时的使命都分派到了别处。你须要面临的只需唯一的主线程,一切 libuv 治理的事情线程都与你断绝开来,无需费心,Node.js 会处置惩罚好那部份。在这个架构之上重 I/O 操纵变得分外高效,那些重 CPU、重内存的也一样。Node.js 供应了开箱即用的异步 I/O 调理,另有一些针对重 CPU 实行的处置惩罚,不过这已超越本文话题领域了。

事宜驱动:基础上,一切当代体系都是主顺序启动终了以后,对每一个收到的请求开启一个历程,接下来依据差别手艺有差别的处置惩罚体式格局,偶然差别会天差地别。典范的完成是:针对一个请求开启一个线程,一步接一步实行使命操纵,假如某个操纵实行迟缓,这个线程上的后续操纵都邑随之挂起,直到一切操纵完成,返回效果。而在 Node.js 中,一切的操纵都注册为一个事宜,守候主顺序或许外部请求来触发。

(体系)运行时:Node.js 运行时是指一切这些代码(上述一切组件,包括底层和上层)供应给 Node.js 运用顺序实行的环境。

合体

我们已相识 Node.js 顶层组件各自的概貌,如今看看它们组合在一起的事情流程,可以更透辟地明白团体架构以及各部份怎样合作交互。

一个 Node.js 运用启动时,V8 引擎会实行你写的运用代码,坚持一份观察者(注册在事宜上的处置惩罚函数)列表。当事宜发作时,它的处置惩罚函数会被加进一个事宜行列。只需这个行列另有守候实行的事宜,事宜轮回就会延续把事宜从行列中拿出,放进挪用客栈。须要注重的是,只需当前一个事宜处置惩罚终了(挪用客栈也已清空),事宜轮回才会把下一个事宜放进挪用客栈。

在挪用客栈中,一切的 I/O 请求都邑转发给 libuv 处置惩罚。libuv 会保持一个线程池,包括四个事情线程(这是默许数目,也可以修正设置增添更多事情线程)。文件体系 I/O 要乞降 DNS 相干请求都邑放进这个线程池处置惩罚;其他的请求,如收集、平台特征相干的请求会分发给响应的体系处置惩罚单位(拜见 libuv 设想概览)。

部署给线程池的这些 I/O 操纵由 Node.js 的底层库实行,完成以后 libuv 把此事宜放回事宜行列,守候主线程实行后续操纵。在 libuv 处置惩罚这些异步 I/O 操纵时期,主线程不会守候处置惩罚效果,而是继承忙其他事变,只需当事宜轮回把 libuv 返回的事宜放进挪用客栈以后,主线程才会继承处置惩罚这个事宜的后续操纵。这就是一个事宜在 Node.js 中实行的全部生命周期。

mbp 曾做过一个奇妙的比方,把 Node.js 算作一家餐厅。我在此借用下他的例子,稍作修正来论述下 Node.js 的实行情况:

把 Node.js 运用顺序设想成一家星巴克,一个训练有素的前台效劳生(唯一的主线程)在柜台前接收定单。当许多主顾同时莅临的时刻,他们列队(进入事宜行列)期待招待;每当效劳生招待一名主顾,效劳生会把定单示知给司理(libuv),司理部署响应的专职职员去烹制咖啡(事情线程或许体系特征)。这个专职职员会使用差别的质料和咖啡机(底层 C/C++ 组件)按定单请求制造咖啡或甜点,一般会有四个如许的专职职员坚持在岗待命(线程池),高峰期的时刻也可以部署更多(不过须要在一早就部署职员来上班,而不能正午暂时关照)。效劳生把定单转交给司理以后不须要等着咖啡制造完成,而是直接最先招待下一名主顾(事宜轮回放进挪用客栈的另一个事宜),你可以把当前挪用客栈里的事宜算作是站在柜台前正在接收效劳的主顾。

当咖啡完成时,会被发送到主顾行列的末了位置,等它移动到柜台前效劳生会叫响应主顾的名字,主顾就来取走咖啡(末了这部份在实在生涯入耳起来有点怪,不过你从顺序实行的角度明白就比较合乎情理了)。

以上就是 Node.js 的内部顶层组件架构概览,以及它的事宜轮回机制。本文依然是异常精简归纳综合,另有许多题目和细节没有睁开,如重 CPU 操纵的处置惩罚、Node.js 设想形式等,将来会有更多文章论述这些内容(译注:在 Aren Li 的 Medium 专栏 Yet Another Node.js Blog 里)。

    原文作者:Amio
    原文地址: https://segmentfault.com/a/1190000005892501
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞