广告
返回顶部
首页 > 资讯 > 前端开发 > html >nodejs是单进程吗
  • 708
分享到

nodejs是单进程吗

2024-04-02 19:04:59 708人浏览 八月长安
摘要

这篇文章主要讲解了“nodejs是单进程吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs是单进程吗”吧! nodej

这篇文章主要讲解了“nodejs是单进程吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs是单进程吗”吧!

nodejs是单进程。node.js采用单线程异步非阻塞模式,也就是说每一个计算独占cpu,遇到“I/O”请求不阻塞后面的计算,当“I/O”完成后,以事件的方式通知,继续执行下一个计算。

nodejs是单进程吗

教程操作环境:windows7系统、nodejs 12.19.0版、Dell G3电脑。

一、多线程与单线程

像java、python这个可以具有多线程的语言。多线程同步模式是这样的,将cpu分成几个线程,每个线程同步运行。

nodejs是单进程吗

而node.js采用单线程异步非阻塞模式,也就是说每一个计算独占cpu,遇到I/O请求不阻塞后面的计算,当I/O完成后,以事件的方式通知,继续执行计算2。

nodejs是单进程吗

事件驱动、异步、单线程、非阻塞I/O,这是我们听得最多的关于nodejs的介绍。看到上面的关键字,可能我们会好奇:

为什么在浏览器中运行的 Javascript 能与操作系统进行如此底层的交互?
nodejs既然是单线程,如何实现异步、非阻塞I/O?
nodejs全是异步调用和非阻塞I/O,就真的不用管并发数了吗?
nodejs事件驱动是如何实现的?和浏览器的event loop是一回事吗?
nodejs擅长什么?不擅长什么?

二、nodejs内部揭秘

要弄清楚上面的问题,首先要弄清楚nodejs是怎么工作的。

nodejs是单进程吗

我们可以看到,Node.js 的结构大致分为三个层次:

1、    Node.js 标准库,这部分是由 javascript 编写的,即我们使用过程中直接能调用的 api。在源码中的 lib 目录下可以看到。

2、    Node bindings,这一层是 Javascript 与底层 C/C++ 能够沟通的关键,前者通过 bindings 调用后者,相互交换数据。

3、这一层是支撑 Node.js 运行的关键,由 C/c++ 实现。
V8:Google 推出的 Javascript VM,也是 Node.js 为什么使用的是 Javascript 的关键,它为 Javascript 提供了在非浏览器端运行的环境,它的高效是 Node.js 之所以高效的原因之一。
Libuv:它为 Node.js 提供了跨平台,线程池,事件池,异步 I/O 等能力,是 Node.js 如此强大的关键。
C-ares:提供了异步处理 DNS 相关的能力。
Http_parser、OpenSSL、zlib 等:提供包括 http 解析、SSL、数据压缩等其他的能力。

三、libuv简介

nodejs是单进程吗

可以看出,几乎所有和操作系统打交道的部分都离不开 libuv的支持。libuv也是node实现跨操作系统的核心所在。

四、我们再来看看最开始我抛出的问题

问题一:为什么在浏览器中运行的 Javascript 能与操作系统进行如此底层的交互?

举个简单的例子,我们想要打开一个文件,并进行一些操作,可以写下面这样一段代码:

var fs = require('fs');
fs.open('./test.txt', "w", function(err, fd) {
    //..do something
});

fs.open = function(path, flags, mode, callback) { 
    // ...
    binding.open(pathModule._makeLong(path),
                        stringToFlags(flags),
 mode,
 callback); 
};

这段代码的调用过程大致可描述为:lib/fs.js → src/node_file.cc →uv_fs

nodejs是单进程吗

从JavaScript调用Node的核心模块,核心模块调用C++内建模块,内建模块通过   libuv进行系统调用,这是Node里经典的调用方式。总体来说,我们在 Javascript 中调用的方法,最终都会通过node-bindings 传递到 C/C++ 层面,最终由他们来执行真正的操作。Node.js 即这样与操作系统进行互动。

问题二:nodejs既然是单线程,如何实现异步、非阻塞I/O?

顺便回答标题nodejs真的是单线程吗?其实只有js执行是单线程,I/O显然是其它线程。
js执行线程是单线程,把需要做的I/O交给libuv,自己马上返回做别的事情,然后libuv在指定的时刻回调就行了。其实简化的流程就是酱紫的!细化一点,nodejs会先从js代码通过node-bindings调用到C/C++代码,然后通过C/C++代码封装一个叫 “请求对象” 的东西交给libuv,这个请求对象里面无非就是需要执行的功能+回调之类的东西,给libuv执行以及执行完实现回调。

总结来说,一个异步 I/O 的大致流程如下:

1、发起 I/O 调用
用户通过 Javascript 代码调用 Node 核心模块,将参数和回调函数传入到核心模块;
Node 核心模块会将传入的参数和回调函数封装成一个请求对象;
将这个请求对象推入到 I/O 线程池等待执行;
Javascript 发起的异步调用结束,Javascript 线程继续执行后续操作。

2、执行回调
I/O 操作完成后,会取出之前封装在请求对象中的回调函数,执行这个回调函数,以完成 Javascript 回调的目的。(这里回调的细节下面讲解)

nodejs是单进程吗

从这里,我们可以看到,我们其实对 Node.js 的单线程一直有个误会。事实上,它的单线程指的是自身 Javascript 运行环境的单线程,Node.js 并没有给 Javascript 执行时创建新线程的能力,最终的实际操作,还是通过 Libuv 以及它的事件循环来执行的。这也就是为什么 Javascript 一个单线程的语言,能在 Node.js 里面实现异步操作的原因,两者并不冲突。

问题三:nodejs全是异步调用和非阻塞I/O,就真的不用管并发数了吗?

之前我们就提到了线程池的概念,发现nodejs并不是单线程的,而且还有并行事件发生。同时,线程池默认大小是 4 ,也就是说,同时能有4个线程去做文件i/o的工作,剩下的请求会被挂起等待直到线程池有空闲。 所以nodejs对于并发数,是由限制的。
线程池的大小可以通过 UV_THREADPOOL_SIZE 这个环境变量来改变 或者在nodejs代码中通过 process.env.UV_THREADPOOL_SIZE来重新设置。

问题四:nodejs事件驱动是如何实现的?和浏览器的event loop是一回事吗?

event loop是一个执行模型,在不同的地方有不同的实现。浏览器和nodejs基于不同的技术实现了各自的event loop。

简单来说:

nodejs的event是基于libuv,而浏览器的event loop则在HTML5的规范中明确定义。
libuv已经对event loop作出了实现,而html5规范中只是定义了浏览器中event loop的模型,具体实现留给了浏览器厂商。

我们上面提到了libuv接过了js传递过来的 I/O请求,那么何时来处理回调呢?

libuv有一个事件循环(event loop)的机制,来接受和管理回调函数的执行。

event loop是libuv的核心所在,上面我们提到 js 会把回调和任务交给libuv,libuv何时来调用回调就是 event loop 来控制的。event loop 首先会在内部维持多个事件队列(或者叫做观察者 watcher),比如 时间队列、网络队列等等,使用者可以在watcher中注册回调,当事件发生时事件转入pending状态,再下一次循环的时候按顺序取出来执行,而libuv会执行一个相当于 while true的无限循环,不断的检查各个watcher上面是否有需要处理的pending状态事件,如果有则按顺序去触发队列里面保存的事件,同时由于libuv的事件循环每次只会执行一个回调,从而避免了 竞争的发生。Libuv的 event loop执行图:

nodejs是单进程吗

nodejs的event loop分为6个阶段,每个阶段的作用如下:
timers:执行setTimeout() 和 setInterval()中到期的callback。
I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行
idle, prepare:仅内部使用
poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
check:执行setImmediate的callback
close callbacks:执行close事件的callback,例如Socket.on("close",func)

event loop的每一次循环都需要依次经过上述的阶段。  每个阶段都有自己的callback队列,每当进入某个阶段,都会从所属的队列中取出callback来执行,当队列为空或者被执行callback的数量达到系统的最大数量时,进入下一阶段。这六个阶段都执行完毕称为一轮循环。

附带event loop 源码:

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
    int timeout;
    int r;
    int ran_pending;
  
    
    r = uv__loop_alive(loop);
    if (!r)
      uv__update_time(loop);
  
    while (r != 0 && loop->stop_flag == 0) {
      uv__update_time(loop);//更新时间变量,这个变量在uv__run_timers中会用到
      uv__run_timers(loop);//timers阶段
      ran_pending = uv__run_pending(loop);//从libuv的文档中可知,这个其实就是I/O callback阶段,ran_pending指示队列是否为空
      uv__run_idle(loop);//idle阶段
      uv__run_prepare(loop);//prepare阶段
  
      timeout = 0;
  
          
      if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
        timeout = uv_backend_timeout(loop);
  
      uv__io_poll(loop, timeout);//poll阶段
      uv__run_check(loop);//check阶段
      uv__run_closing_handles(loop);//close阶段
      //如果mode == UV_RUN_ONCE(意味着流程继续向前)时,在所有阶段结束后还会检查一次timers,这个的逻辑的原因不太明确
      
      if (mode == UV_RUN_ONCE) {
        uv__update_time(loop);
        uv__run_timers(loop);
      }
  
      r = uv__loop_alive(loop);
      if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWaiT)
        break;
    }
  
    if (loop->stop_flag != 0)
      loop->stop_flag = 0;
  
    return r;
  }

这里我们再详细了解一下poll阶段:

poll 阶段有两个主要功能:
1、执行下限时间已经达到的timers的回调
2、处理 poll 队列里的事件。

当event loop进入 poll 阶段,并且 没有设定的timers(there are no timers scheduled),会发生下面两件事之一:

1、如果 poll 队列不空,event loop会遍历队列并同步执行回调,直到队列清空或执行的回调数到达系统上限;

2、如果 poll 队列为空,则发生以下两件事之一:
(1)如果代码已经被setImmediate()设定了回调, event loop将结束 poll 阶段进入 check 阶段来执行 check 队列(里的回调)。
(2)如果代码没有被setImmediate()设定回调,event loop将阻塞在该阶段等待回调被加入 poll 队列,并立即执行。

但是,当event loop进入 poll 阶段,并且 有设定的timers,一旦 poll 队列为空(poll 阶段空闲状态):
event loop将检查timers,如果有1个或多个timers的下限时间已经到达,event loop将绕回 timers 阶段。

event loop的一个例子讲述:

var fs = require('fs');

function someAsyncOperation (callback) {
  // 假设这个任务要消耗 95ms
  fs.readFile('/path/to/file', callback);
}

var timeoutScheduled = Date.now();

setTimeout(function () {

  var delay = Date.now() - timeoutScheduled;

  console.log(delay + "ms have passed since I was scheduled");
}, 100);

// someAsyncOperation要消耗 95 ms 才能完成
someAsyncOperation(function () {

  var startCallback = Date.now();

  // 消耗 10ms...
  while (Date.now() - startCallback < 10) {
    ; // do nothing
  }

});

当event loop进入 poll 阶段,它有个空队列(fs.readFile()尚未结束)。所以它会等待剩下的毫秒,直到最近的timer的下限时间到了。当它等了95ms,fs.readFile()首先结束了,然后它的回调被加到 poll的队列并执行——这个回调耗时10ms。之后由于没有其它回调在队列里,所以event loop会查看最近达到的timer的下限时间,然后回到 timers 阶段,执行timer的回调。

所以在示例里,回调被设定 和 回调执行间的间隔是105ms。

到这里我们再总结一下,整个异步IO的流程:

nodejs是单进程吗

问题五、nodejs擅长什么?不擅长什么?

Node.js 通过 libuv 来处理与操作系统的交互,并且因此具备了异步、非阻塞、事件驱动的能力。因此,NodeJS能响应大量的并发请求。所以,NodeJS适合运用在高并发、I/O密集、少量业务逻辑的场景。

上面提到,如果是 I/O 任务,Node.js 就把任务交给线程池来异步处理,高效简单,因此 Node.js 适合处理I/O密集型任务。但不是所有的任务都是 I/O 密集型任务,当碰到CPU密集型任务时,即只用CPU计算的操作,比如要对数据加解密(node.bcrypt.js),数据压缩和解压(node-tar),这时 Node.js 就会亲自处理,一个一个的计算,前面的任务没有执行完,后面的任务就只能干等着 。我们看如下代码:

var start = Date.now();//获取当前时间戳
setTimeout(function () {
    console.log(Date.now() - start);
    for (var i = 0; i < 1000000000; i++){//执行长循环
    }
}, 1000);
setTimeout(function () {
    console.log(Date.now() - start);
}, 2000);

最终我们的打印结果是:(结果可能因为你的机器而不同)
1000
3738

对于我们期望2秒后执行的setTimeout函数其实经过了3738毫秒之后才执行,换而言之,因为执行了一个很长的for循环,所以我们整个Node.js主线程被阻塞了,如果在我们处理100个用户请求中,其中第一个有需要这样大量的计算,那么其余99个就都会被延迟执行。如果操作系统本身就是单核,那也就算了,但现在大部分服务器都是多 CPU 或多核的,而 Node.js 只有一个 EventLoop,也就是只占用一个 CPU 内核,当 Node.js 被CPU 密集型任务占用,导致其他任务被阻塞时,却还有 CPU 内核处于闲置状态,造成资源浪费。

其实虽然Node.js可以处理数以千记的并发,但是一个Node.js进程在某一时刻其实只是在处理一个请求。

因此,Node.js 并不适合 CPU 密集型任务。

感谢各位的阅读,以上就是“nodejs是单进程吗”的内容了,经过本文的学习后,相信大家对nodejs是单进程吗这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: nodejs是单进程吗

本文链接: https://www.lsjlt.com/news/85846.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • nodejs是单进程吗
    这篇文章主要讲解了“nodejs是单进程吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs是单进程吗”吧! nodej...
    99+
    2022-10-19
  • NodeJS中的模块是单例吗
    本篇内容介绍了“NodeJS中的模块是单例吗”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!NodeJS的模...
    99+
    2022-10-19
  • nodejs是多线程吗
    这篇文章主要介绍“nodejs是多线程吗”,在日常操作中,相信很多人在nodejs是多线程吗问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”nodejs是多线程吗”的疑惑有所帮...
    99+
    2022-10-19
  • nodejs多线程还是单线程
    Node.js 多线程还是单线程?在开发领域中,Node.js 的居民们时常被问到这个问题:Node.js 多线程还是单线程? 我们来一探究竟。Node.js 是一个开放源代码、跨平台的、基于 Chrome V8 引擎建立的 JavaScr...
    99+
    2023-05-17
  • 为什么nodejs是单线程的
    在开发Web应用或者服务器端应用时,Node.js已成为了一种非常流行的选择。Node.js是一个基于Chrome V8 JavaScript引擎的开源平台,它允许在服务器端使用JavaScript编写高性能、可扩展的应用程序。然而,相对于...
    99+
    2023-05-18
  • nodejs是一门语言吗
    这篇文章主要讲解了“nodejs是一门语言吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs是一门语言吗”吧! nod...
    99+
    2022-10-19
  • hbuilderx是用nodejs编译吗
    HBuilderX是一款由DCloud开发的优秀的跨平台专业开发工具,旨在为开发者提供更加高效便利的开发体验。与其他的开发工具不同的是,HBuilderX使用了多种技术组合,其中包括Node.js。那么,HBuilderX是否是用Node....
    99+
    2023-05-14
  • nodejs jade是静态页吗
    Node.js 和 Jade 是两个不同的概念,在 Web 开发中,Node.js 通常作为后端编程语言而 Jade 则是一种前端渲染引擎,用来生成 HTML 静态页面。所以,Jade 并不是静态页,它是根据后端传递的数据,将预定义的模板进...
    99+
    2023-05-14
  • NodeJS进程是怎么退出的
    本篇内容介绍了“NodeJS进程是怎么退出的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录前言主动退出Exceptions, Rejec...
    99+
    2023-06-20
  • Node.js是不是单线程吗
    这篇文章主要为大家展示了“Node.js是不是单线程吗”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Node.js是不是单线程吗”这篇文章吧。进程在操作系统中,...
    99+
    2022-10-19
  • nodejs是一个服务器吗
    这篇文章主要讲解了“nodejs是一个服务器吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs是一个服务器吗”吧! n...
    99+
    2022-10-19
  • Node.js是单线程的程序吗
    这篇文章主要讲解了“Node.js是单线程的程序吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Node.js是单线程的程序吗”吧!Node.js 是单线程...
    99+
    2022-10-19
  • nodejs中process进程的作用是什么
    这期内容当中小编将会给大家带来有关nodejs中process进程的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。  进程模块  process模块允许你获得或...
    99+
    2022-10-19
  • zkfc是hadoop进程吗
    是的,`zkfc`是Hadoop中的一个进程。它是ZooKeeper Failover Controller(ZKFC)的缩写。在H...
    99+
    2023-10-11
    hadoop
  • golang是多进程吗
    golang是多进程,其线程模型是MPG模型,整体上Go程与内核线程是多对多对应的,因此首先来讲就一定是多线程的。Golang有些所谓的M比N模型,M个线程下可以创建N个go routine,一般而言N远大于M,本质上属于多线程模型,但是协...
    99+
    2023-07-10
  • Nodejs如何关闭进程
    这篇“Nodejs如何关闭进程”除了程序员外大部分人都不太理解,今天小编为了让大家更加理解“Nodejs如何关闭进程”,给大家总结了以下内容,具有一定借鉴价值,内容详细步骤清晰,细节处理妥当,希望大家通过这篇文章有所收获,下面让我们一起来看...
    99+
    2023-06-14
  • 详细谈谈NodeJS进程是如何退出的
    目录前言主动退出 Exceptions, Rejections 和 Emitted Errors 信号 小结 前言 有几种因素可以导致 NodeJS 进程退出。在这些因素中,有些是可...
    99+
    2022-11-12
  • php异步在单进程中很重要吗
    这篇文章主要介绍“php异步在单进程中很重要吗”,在日常操作中,相信很多人在php异步在单进程中很重要吗问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”php异步在单进程中很重要吗”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-20
  • 详解nodejs中的process进程
    虽然node对操作系统做了很多抽象的工作,但是你还是可以直接和他交互,比如和系统中已经存在的进程进行交互,创建工作子进程。node是一个用于事件循环的线程,但是你可以在这个事件循环之外创建其他的进程(线程)...
    99+
    2022-06-04
    详解 进程 nodejs
  • Nodejs极简入门教程(三):进程
    Node 虽然自身存在多个线程,但是运行在 v8 上的 JavaScript 是单线程的。Node 的 child_process 模块用于创建子进程,我们可以通过子进程充分利用 CPU。范例: var ...
    99+
    2022-06-04
    进程 入门教程 Nodejs
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作