什么是node.js?
根据Node的官方文档,Node.js是在Google的开源V8 JavaScript引擎上构建的JavaScript运行时。 Node.js可以在服务器端执行JavaScript代码,从而可以创建快速,可扩展,高性能网络应用程序。
node.js的历史
node.js是2009年的introduced, Ryan Dahl 。尽管最初怀疑,但该概念在2011年在2011年2月22日在旧金山举行的PHP聚会上介绍了Node.js。video流行了,引起人们的注意,引起人们注意在服务器端使用JavaScript。在那个video中,瑞安·达尔(Ryan Dahl)在一群程序员面前解释了node.js。他解释了他如何在Google Chrome的V8引擎的帮助下开发新的JavaScript运行时,以及如何在服务器端使用JavaScript。在此之前,JavaScript仅在客户端和浏览器侧使用。在收听了Ryan的解释之后,大多数程序员都取笑了他,因为他们无法接受JavaScript作为服务器端语言。当时,存在一些热服务器端语言,例如Ruby on Rails。但是瑞安·达尔(Ryan Dahl)没有停止。 Dahl看到了在服务器端使用JavaScript并开发和改进Node.js作为解决方案的潜力。
node.js的普及背后的原因
有一些关键事件有助于节点获得其流行。
- 在 2010 中,以撒Z. Schlueter创建了npm,用于Node Package Manager的缩写。当时,Node.js是一个相对较新的平台,缺乏像其他编程语言中使用的标准软件包管理器。它使开发人员更容易与其他开发人员共享代码,并在其项目中重复使用现有软件包。
- 在 2007 中,德怀特·梅里曼(Dwight Merriman)和艾略特·霍洛维茨(Eliot Horowitz)创造了mongodb。它使用JSON风格的二进制数据结构来存储数据,该数据易于与JavaScript一起使用。正如预期的那样,开发人员社区不接受像MongoDB这样的NOSQL数据库,因为SQL关系数据库当时很受欢迎。随着Twitter和Facebook等社交媒体公司开始以JSON格式发布API,随着开发人员发现使用关系数据库和JSON-Format数据更具挑战性,JSON风格的数据结构增加了。结果,MongoDB和Node.js等解决方案被更广泛地采用。
- node.js使开发人员更容易在其Web应用程序中使用JavaScript和后端。这种功能一直是Node.J.开发人员受欢迎程度的重要因素。通过在客户端和服务器侧使用相同的语言,Node.js消除了在多种编程语言之间切换的需求,从而导致更简化和有效的开发。此外,由于JavaScript是一种广泛使用的语言,因此许多开发人员已经拥有它的经验,使使用Node.js进行服务器端开发的过渡是自然的选择。这些因素已导致Node.js成为快速,可扩展性和高性能网络应用程序的首选解决方案。
Node.js的架构并了解V8引擎
让我们了解有关节点依赖性的节点架构。
node.js有几个依赖项可以正常工作/功能。最重要的是V8引擎和Libuv。我们早些时候看到Node.js建立在Chrome的V8发动机上。首先,我们必须在这里知道V8引擎的作用。
我们许多人为浏览器编写JavaScript。当我们编写代码时,机器不直接理解我们的代码。机器必须将其转换为机器代码,以了解我们的命令。浏览器中有引擎可以将我们的代码视为机器代码。不同的浏览器具有不同的引擎,可以理解/将JavaScript代码转换为机器代码。这里有一些示例:
- Google Chrome:V8
- Mozilla Firefox:蜘蛛猴
- Apple Safari:JavascriptCore
- Microsoft Edge:脉轮
- opera:眨眼(基于与Google Chrome相同的引擎,V8)
最强大的是Chrome的V8引擎。
在服务器端,必须有一些将JavaScript代码转换为机器代码的东西。为此,瑞安·达尔(Ryan Dahl)在Node.js中使用了超速V8引擎。现在,node.js可以将JavaScript代码转换为机器代码。这就是节点中V8引擎的用例。
Libuv图书馆
V8引擎还不够孤单,无法运行Node.js等服务器端框架。这是另一个称为libuv的关键库。这是一个开源库,强烈关注异步IO(输入/输出)。
libuv,node.js可以访问基础计算机操作系统,文件系统,与网络相关的内容等等。
libuv实施 node.js的两个关键特征
- 事件循环,它可以处理轻松的任务,例如回调或网络io
- 线程池,处理诸如访问文件或文件压缩之类的重型工作。
我将稍后讨论这两个详细内容。
Node.js是完全用JavaScript编写的吗?
我们直观地认为幕后Node.js中只有JavaScript代码,因为Node.js是JavaScript运行时。但事实并非如此。正如我所说,Libuv是节点的重要组成部分,Libuv完全用C ++编写。另一方面,V8还用JS&C ++编写。因此,节点不仅用JavaScript编写,还用C ++编写。但是有一些抽象层,因此我们可以通过纯JavaScript函数访问节点中的所有功能和所有功能。我们不必弄乱C ++或任何基础代码。例如,对于文件读取,我们编写一个纯JS函数以从文件系统中读取,哪个功能在c ++中编写。非常有趣。
要注意的一个重要的事情是Node.js不仅取决于V8引擎和Libuv。还有其他图书馆,例如HTTP解析器(用于解析HTTP),C-ares(用于处理DNS请求的内容),OpenSSL(用于密码学),Zlib(用于文件压缩),等等。
现在让我们看看什么是线程池。
线程和线程池
每当我们使用node.js时,在计算机上启动节点进程。此过程代表当前执行的程序。
在该过程中,节点在单个线程中运行。线程就像较大的程序中的一个小程序一样,一系列指令序列可以同时执行多个任务。就像是执行代码的框一样。
必须清楚地了解多线程和单线的概念。多线程可以同时执行多个操作,其中几个任务可以同时在后台运行。另一方面,单线线仅允许一次执行一个操作。换句话说,它以线性序列运行,无法同时执行多个操作。
重要的是要了解节点在单个线程上运行,无论我们有多少用户。这意味着如果单程被阻塞,则整个应用程序可能会受到影响。因此,我们需要特别注意可能阻止线程的动作。
让我们看看节点在单个线程中运行时会发生什么。
运行节点应用程序时,程序将通过多个阶段。 node.js 初始化程序,执行所有顶级代码,需要必要的模块,然后注册事件回调。
所有这些步骤完成后,事件循环开始运行。事件循环,大部分应用程序完成的工作。这是应用程序的核心。但是,某些任务可能是资源密集的和沉重的。如果在事件循环中执行,它们可能会阻止单线线程。在事件循环外处理这些沉重的任务对于避免这种情况很重要。
在这里,线程池来救援,Libuv库提供了。事件循环将这些繁重的任务卸载到线程池。它发生在现场。我们不必担心哪些任务会进入线程池,哪些任务不会。
线程池有四个与单个主线程分开的其他线程。我们可以配置最多128个线程的线程池。但是,通常,我们不需要。四个线程就足够了。
以下是一些任务示例,这些示例已从事件循环中卸载到线程池 -
- 使用文件系统API 的任务:当我们执行读取或编写文件之类的操作时,这些任务可能需要大量时间。他们可以阻止单个线程并引起性能问题。
- 密码相关的任务:密码学涉及复杂的数学计算。在事件循环时执行这些操作可以减慢我们的应用程序。
- 压缩任务:当我们需要压缩大型数据(例如图像或视频)时,这些任务可能会花费大量时间,并且还可以减慢事件循环。因此,这些任务被卸载到线程池以防止此。
- DNS查找:当我们需要将域名解析到IP地址中时,它是通过DNS查找过程完成的。此过程也可能需要时间并阻止事件循环,这可能会导致性能问题。因此,为防止这种情况,这些任务被卸载到线程池,在那里它们可以同时运行而无需阻止主线程。
事件循环及其在node.js中的实现
现在,我们在node.js进程中。此过程包含事件循环运行的线程。事件循环是Node.js进程的重要组成部分。它负责在构成node.js应用程序的回调函数中执行所有代码。
正如我们之前了解到的,某些任务也可能被卸载到线程池以进行有效处理。但是,事件循环是node.js运行时的中心机制。 Node.js基于回调驱动的架构。这意味着一旦某个事件完成或发射,功能就会立即调用。这是由于node.js。
的事件驱动架构而发生的。事件驱动的架构
事件驱动的体系结构是一个系统,在该系统中,事件循环发出,拾取和处理事件。然后事件循环执行关联的回调函数到该事件。
例如,诸如新的HTTP请求,计时器到期或完成的文件读取或写入文件操作之类的事件将发出事件,然后将其填充事件。然后,它通过执行关联的回调函数来处理这些事件。事件循环观察事件并在幕后执行必要的编排。
让我们仔细研究事件循环在场景后面的实际运行方式:
node.js应用程序启动后,事件循环开始运行。事件循环有几个阶段。每个阶段都有自己的回调队列。有人可能会说活动循环只有一个回调或事件队列。但是它有多个阶段,每个阶段都有自己的回调队列。
事件循环的四个最重要阶段如下:
-
处理过期的计时器回调(settimeout()):第一阶段涉及从过期的计时器执行回调。如果计时器在其他任何阶段期间到期,则将在当前阶段完成后立即执行其关联的回调函数,事件循环返回其第一阶段。
-
i/o i/o回调的轮询和执行:节点io表示网络或文件系统诸如文件读取,写入新文件等,以及民意调查的意思是寻找准备处理的新IO事件,并将其放在回调队列中。此阶段是执行申请代码99%的地方。
-
执行setImMediate回调:这是一种特殊类型的计时器,允许在执行I/O回调后立即执行代码。
-
执行密切回调:事件循环处理此阶段的所有关闭事件。这包括关闭Web服务器或Web插座等事件。
除了这四个阶段外,其他两个队列也很重要:
process.nexttick()队列:该队列在当前阶段完成后立即执行其回调。它类似于setimmediate回调。唯一的区别是SetimMediate回调仅在执行I/O回调后立即执行,但是Process.nexttick()在任何阶段后立即执行。
用于解决诺言的微型队列:该队列在当前阶段完成后立即执行回调,就像process.nexttick()队列一样。如果这两个队列中的任何一个都有任何回调,则将在当前阶段完成后立即执行它们,而不是等待整个事件循环完成其四个阶段。例如,如果承诺在运行过期计时器的回调时从API调用中解析并返回数据,则将在计时器回调完成后立即执行其回调。
前四个阶段中的每个阶段中的每个阶段,事件循环检查这两个队列中是否有任何回调。如果有的话,它们将立即执行。这完成了事件循环的一个刻度。刻度定义为循环的一个周期。一个周期后,Node.js运行时检查是否有任何已待定的计时器或I/O任务,或任何阶段的任何回调。如果有,则循环再次运行。否则,申请退出。
由于事件循环,在node.js中使异步编程成为可能,这使事件循环成为node.js的最重要功能。它允许Node.js以单个线程运行,使其轻巧且快速,但也会带来一些潜在的风险。由于Node.js是一个单线程程序,因此有可能意外阻止我们的应用程序。因此,在编写代码时要格外小心以避免不小心阻止应用程序。
避免阻止事件循环的步骤
这里有一些提示,可以避免在Node.js中阻止代码:
-
避免使用函数的同步版本;在任何回调函数之外编写任何必要的同步代码。这样,将在事件循环启动之前执行代码。
-
避免复杂的计算,例如Node.js中的嵌套环,因为它们可能导致应用程序被阻止。
-
在处理大型JSON对象时要谨慎,因为解析或串起它们可能会耗时。
-
避免使用复杂的正则表达式,因为它们需要大量的处理能力,这些功率可以是资源密集型并减慢事件循环的速度。如果我们使用复杂的正则表达式,则可能需要大量时间来执行。这就是为什么还建议将大型正则表达式分解为更较小的,可以更有效地执行的较小的正则表达式。这将确保事件循环继续顺利运行,并且该应用程序保持响应能力。
-
在事件循环中不要执行任何CPU密集型任务。它可能导致应用程序被阻止。这是一些CPU密集型任务示例 -
-
重型计算,例如数学计算和科学模拟
-
图像和视频处理,例如调整大小,裁剪和过滤图像和视频
-
加密操作,例如数据的加密和解密
-
数据压缩和解压缩
-
如果要在node.js中执行这些任务,请务必要小心。我们将不得不采取额外的步骤来最大程度地减少它们对事件循环的影响。这可以通过手动将这些任务移动到单独的过程或线程或使用用于执行这些类型操作的外部库来完成。
此外,重要的是要以不给事件循环施加压力的方式构建应用程序代码,例如避免复杂的算法,最大程度地减少阻塞操作并使用异步编程技术尽可能。这将有助于确保我们的node.js应用程序即使在重负载下也保持迅速,高效和响应。
今天!
恭喜您完成了本文!您现在对Node.js有了更深入的了解。无论是初学者还是经验丰富的开发人员,此信息对于帮助您构建高效,可扩展和高性能应用程序都是有价值的。
如果您喜欢阅读本文,则可以在Twitter上与我联系或查看我的other articles。此外,您可以订阅我的non-tech newsletter,在那里我分享我对生活,生产力等的想法,以及我每个月遇到的最佳内容。
快乐学习!