我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Node的多进程服务如何实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Node的多进程服务如何实现

这篇文章主要介绍“Node的多进程服务如何实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node的多进程服务如何实现”文章能帮助大家解决问题。

    我们现在已经知道了Node是单线程运行的,这表示潜在的错误有可能导致线程崩溃,然后进程也会随着退出,无法做到企业追求的稳定性;另一方面,单进程也无法充分多核CPU,这是对硬件本身的浪费。Node社区本身也意识到了这一问题,于是从0.1版本就提供了child_process模块,用来提供多进程的支持。

    1. child_process 模块

    child_process模块中包括了很多创建子进程的方法,包括forkspawnexecexecFile等等。它们的定义如下:

    • child_process.exec(command[, options][, callback])

    • child_process.spawn(command[, args][, options])

    • child_process.fork(modulePath[, args][, options])

    • child_process.execFile(file[, args][, options][, callback])

    在这4个API中以spawn最为基础,因为其他三个API或多或少都是借助spawn实现的。

    2. spawn

    spawn方法的声明格式如下:

    child_process.spawn(command[, args][, options])

    spawn方法会使用指定的command来生成一个新进程,执行完对应的command后子进程会自动退出。

    该命令返回一个child_process对象,这代表开发者可以通过监听事件来获得命令执行的结果。

    下面我们使用spwan来执行ls命令:

    const spawn = require('child_process').spawn;const ls = spawn('ls', ['-1h', '/usr']);ls.stdout.on('data', (data) => {    console.log('stdout: ', daata.toString());});ls.stderr.on('data', (data) => {    console.log('stderr: ', daata.toString());});ls.on('close', (code) => {    console.log('child process exited with code', code);});

    其中spawn的第一个参数虽然是command,但实际接收的却是一个file,可以在Linux或者Mac OSX上运行,这是由于ls命令也是以可执行文件形式存在的。

    类似的,在Windows系统下我们可以试着使用dir命令来实现功能类似的代码:

    const spawn = require('child_process').spawn;const ls = spawn('dir');ls.stdout.on('data', (data) => {    console.log('stdout: ', daata.toString());});

    然而在Windows下执行上面代码会出现形如Error:spawn dir ENOENT的错误。

    原因就在于spawn实际接收的是一个文件名而非命令,正确的代码如下:

    const spawn = require('child_process').spawn;const ls = spawn('powershell', ['dir']);ls.stdout.on('data', (data) => {    console.log('stdout: ', daata.toString());});

    这个问题的原因与操作系统本身有关,在Linux中,一般都是文件,命令行的命令也不例外,例如ls命令是一个名为ls的可执行文件;而在Windows中并没有名为dir的可执行文件,需要通过cmd或者powershell之类的工具提供执行环境。

    3. fork

    在Linux环境下,创建一个新进程的本质是复制一个当前的进程,当用户调用 fork 后,操作系统会先为这个新进程分配空间,然后将父进程的数据原样复制一份过去,父进程和子进程只有少数值不同,例如进程标识符(PD)。

    对于 Node 来说,父进程和子进程都有独立的内存空间和独立的 V8 实例,它们和父进程唯一的联系是用来进程间通信的 IPC Channel。

    此外,Node中fork和 POSIX 系统调用的不同之处在于Node中的fork并不会复制父进程。

    Node中的fork是上面提到的spawn的一种特例,前面也提到了Node中的fork并不会复制当前进程。多数情况下,fork接收的第一个参数是一个文件名,使用fork("xx.js")相当于在命令行下调用node xx.js,并且父进程和子进程之间可以通过process.send方法来进行通信。

    下面我们来看一个简单的栗子:

    // master.js 调用 fork 来创建一个子进程const child_process = require('child_process');const worker = child_process.fork('worker.js', ['args1']);worker.on('exit', () => {  console.log('child process exit');});worker.send({ msg: 'hello child' });worker.on('message', msg => {  console.log('from child: ', msg);});// worker.jsconst begin = process.argv[2];console.log('I am worker ' + begin);process.on('message', msg => {  console.log('from parent ', msg);  process.exit();});process.send({ msg: 'hello parent' });

    fork内部会通过spawn调用process.executePath,即Node的可执行文件地址来生成一个Node实例,然后再用这个实例来执行fork方法的modulePath参数。

    输出结果为:

    I am worker args1
    from parent  { msg: 'hello child' }
    from child:  { msg: 'hello parent' }
    child process exit

    4. exec 和 execFile

    如果我们开发一种系统,那么对于不同的模块可能会用到不同的技术来实现,例如 Web服务器使用 Node ,然后再使用 Java 的消息队列提供发布订阅服务,这种情况下通常使用进程间通信的方式来实现。

    但有时开发者不希望使用这么复杂的方式,或者要调用的干脆是一个黑盒系统,即无法通过修改源码来进行来实现进程间通信,这时候往往采用折中的方式,例如通过 shell 来调用目标服务,然后再拿到对应的输出。

    child_process提供了一个execFile方法,它的声明如下:

    child_process.execFile(file, args, options, callback)

    说明:

    • file {String}要运行的程序的文件名

    • args {Array}字符串参数列表

    • options {Object}

      • cwd {String}子进程的当前工作目录

      • env {Object}环境变量键值对

      • encoding {String}编码(默认为 'utf8'

      • timeout {Number}超时(默认为 0)

      • maxBuffer {Number}缓冲区大小(默认为 200*1024)

      • killSignal {String}结束信号(默认为'SIGTERM'

    • callback {Function}进程结束时回调并带上输出

      • error {Error}

      • stdout {Buffer}

      • stderr {Buffer}

      • 返回:ChildProcess对象

    可以看出,execfilespawn在形式上的主要区别在于execfile提供了一个回调函数,通过这个回调函数可以获得子进程的标准输出/错误流。

    使用 shell 进行跨进程调用长久以来被认为是不稳定的,这大概源于人们对控制台不友好的交互体验的恐惧(输入命令后,很可能长时间看不到一个输出,尽管后台可能在一直运算,但在用户看来和死机无异)。

    在 Linux下执行exec命令后,原有进程会被替换成新的进程,进而失去对新进程的控制,这代表着新进程的状态也没办法获取了,此外还有 shell 本身运行出现错误,或者因为各种原因出现长时间卡顿甚至失去响应等情况。

    Node.js 提供了比较好的解决方案,timeout解决了长时间卡顿的问题,stdoutstderr则提供了标准输出和错误输出,使得子进程的状态可以被获取。

    5. 各方法之间的比较

    5.1 spawn 和 execFile

    为了更好地说明,我们先写一段简单的 C 语言代码,并将其命名为 example.c:

    #include<stdio.h>int main() {    printf("%s", "Hello World!");    return 5;}

    使用 gcc 编译该文件:

    gcc example.c -o example

    生成名为example的可执行文件,然后将这个可执行文件放到系统环境变量中,然后打开控制台,输入example,看到最后输出"Hello World"

    确保这个可执行文件在任意路径下都能访问。

    我们分别用spawnexecfile来调用example文件。

    首先是spawn

    const spawn = require('child_process').spawn;const ls = spawn('example');ls.stdout.on('data', (data) => {    console.log('stdout: ', daata.toString());});ls.stderr.on('data', (data) => {    console.log('stderr: ', daata.toString());});ls.on('close', (code) => {    console.log('child process exited with code', code);});

    程序输出:

    stdout: Hello World!
    child process exited with code 5

    程序正确打印出了Hello World,此外还可以看到example最后的return 5会被作为子进程结束的code被返回。

    然后是execFile

    const exec = require('child_process').exec;const child = exec('example', (error, stdout, stderr) => {    if (error) {        throw error;    }    console.log(stdout);});

    同样打印出Hello World,可见除了调用形式不同,二者相差不大。

    5.2 execFile 和 spawn

    在子进程的信息交互方面,spawn使用了流式处理的方式,当子进程产生数据时,主进程可以通过监听事件来获取消息;而exec是将所有返回的信息放在stdout里面一次性返回的,也就是该方法的maxBuffer参数,当子进程的输出超过这个大小时,会产生一个错误。

    此外,spawn有一个名为shell的参数:

    其类型为一个布尔值或者字符串,如果这个值被设置为true,,就会启动一个 shell 来执行命令,这个 shell 在 UNIX上是 bin/sh,,在Windows上则是cmd.exe。

    5.3 exec 和 execFile

    exec在内部也是通过调用execFile来实现的,我们可以从源码中验证这一点,在早期的Node源码中,exec命令会根据当前环境来初始化一个 shell,,例如 cmd.exe 或者 bin/sh,然后在shell中调用作为参数的命令。

    通常execFile的效率要高于exec,这是因为execFile没有启动一个 shell,而是直接调用 spawn来实现的。

    6. 进程间通信

    前面介绍的几个用于创建进程的方法,都是属于child_process的类方法,此外childProcess类继承了EventEmitter,在childProcess中引入事件给进程间通信带来很大的便利。

    childProcess中定义了如下事件。

    • Event:'close':进程的输入输出流关闭时会触发该事件。

    • Event:'disconnect':通常childProcess.disconnect调用后会触发这一事件。

    • Event:'exit':进程退出时触发。

    • Event:'message':调用child_process.send会触发这一事件

    • Event:'error':该事件的触发分为几种情况:

      • 该进程无法创建子进程。

      • 该进程无法通过kill方法关闭。

      • 无法发送消息给子进程。

    Event:'error'事件无法保证一定会被触发,因为可能会遇到一些极端情况,例如服务器断电等。

    上面也提到,childProcess模块定义了send方法,用于进程间通信,该方法的声明如下:

    child.send(message[, sendHandle[, options]][, callback])

    通过send方法发送的消息,可以通过监听message事件来获取。

    // master.js 父进程向子进程发送消息const child_process = require('child_process');const worker = child_process.fork('worker.js', ['args1']);worker.on('exit', () => {  console.log('child process exit');});worker.send({ msg: 'hello child' });worker.on('message', msg => {  console.log('from child: ', msg);});// worker.js 子进程接收父进程消息const begin = process.argv[2];console.log('I am worker ' + begin);process.on('message', msg => {  console.log('from parent ', msg);  process.exit();});process.send({ msg: 'hello parent' });

    send方法的第一个参数类型通常为一个json对象或者原始类型,第二个参数是一个句柄,该句柄可以是一个net.Socket或者net.Server对象。下面是一个例子:

    //master.js 父进程发送一个 Socket 对象const child = require('child_process').fork('worker.js');// Open up the server object and send the handle.const server = require('net').createServer();server.on('connection', socket => {  socket.end('handled by parent');});server.listen(1337, () => {  child.send('server', server);});//worker.js 子进程接收 Socket 对象process.on('message', (m, server) => {  if (m === 'server') {    server.on('connection', socket => {      socket.end('handled by child');    });  }});

    7. Cluster

    前面已经介绍了child_process的使用,child_process的一个重要使用场景是创建多进程服务来保证服务稳定运行。

    为了统一 Node 创建多进程服务的方式,Node 在之后的版本中增加了Cluster模块,Cluster可以看作是做了封装的child_Process模块。

    Cluster模块的一个显著优点是可以共享同一个socket连接,这代表可以使用Cluster模块实现简单的负载均衡

    下面是Cluster的简单栗子:

    const cluster = require('cluster');const http = require('http');const numCPUs = require('os').cpus().length;if (cluster.isMaster) {  console.log('Master process id is', process.pid);  // Fork workers.  for (let i = 0; i < numCPUs; i++) {    cluster.fork();  }  cluster.on('exit', (worker, code, signal) => {    console.log('worker process died, id ', worker.process.pid);  });} else {  // Worker 可以共享同一个 TCP 连接  // 这里的例子是一个 http 服务器  http.createServer((req, res) => {    res.writeHead(200);    res.end('hello world\n');  }).listen(8000);  console.log('Worker started, process id', process.pid);}

    上面是使用Cluster模块的一个简单的例子,为了充分利用多核CPU,先调用OS模块的cpus()方法来获得CPU的核心数,假设主机装有两个 CPU,每个CPU有4个核,那么总核数就是8。

    在上面的代码中,Cluster模块调用fork方法来创建子进程,该方法和child_process中的fork是同一个方法。

    Cluster模块采用的是经典的主从模型,由master进程来管理所有的子进程,可以使用cluster.isMaster属性判断当前进程是master还是worker,其中主进程不负责具体的任务处理,其主要工作是负责调度和管理,上面的代码中,所有的子进程都监听8000端口。

    通常情况下,如果多个 Node 进程监听同一个端口时会出现Error: listen EADDRINUS的错误,而Cluster模块能够让多个子进程监听同一个端口的原因是master进程内部启动了一个 TCP 服务器,而真正监听端口的只有这个服务器,当来自前端的请求触发服务器的connection事件后,master会将对应的socket句柄发送给子进程。

    关于“Node的多进程服务如何实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Node的多进程服务如何实现

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

    下载Word文档

    猜你喜欢

    Node的多进程服务如何实现

    这篇文章主要介绍“Node的多进程服务如何实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node的多进程服务如何实现”文章能帮助大家解决问题。我们现在已经知道了Node是单线程运行的,这表示潜在
    2023-07-01

    Node多进程的实现方法

    我们都知道 Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能
    2022-11-13

    小程序如何实现多进程

    这篇文章将为大家详细讲解有关小程序如何实现多进程,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。前言小程序这个名词相信大家已经不陌生了,继微信之后,阿里巴巴、百度、头条等大厂相继实现了自己的小
    2023-06-04

    Node多进程模型和项目如何部署

    这篇文章主要介绍“Node多进程模型和项目如何部署”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node多进程模型和项目如何部署”文章能帮助大家解决问题。进程 VS 线程进程进程(process)是
    2023-07-04

    Node中的进程和线程怎么实现

    这篇文章主要介绍了Node中的进程和线程怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Node中的进程和线程怎么实现文章都会有所收获,下面我们一起来看看吧。一、进程和线程1.1、专业性文字定义进程(Pr
    2023-07-04

    Python中的多进程编程是如何实现的?

    Python中的多进程编程是如何实现的?Python是一门简洁而高效的编程语言,而在处理大量数据或者需要同时执行多个任务时,单线程的程序可能显得效率不高。为了解决这个问题,Python提供了多进程编程的支持,允许开发者同时执行多个进程来提高
    2023-10-27

    php如何实现多进程和关闭进程的方法

    本篇内容介绍了“php如何实现多进程和关闭进程的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!php实现关闭进程的方法:首先创建一个PH
    2023-06-07

    Python使用multiprocessing如何实现多进程

    本文探讨了使用Pythonmultiprocessing模块实现多进程的方法,提供了一个示例来说明如何创建并启动进程。它还介绍了进程间通信机制,例如队列、管道和共享内存。多进程的优点包括并发性、隔离和可扩展性,但缺点包括开销、复杂性和调试困难。文中还提供了最佳实践,例如限制进程数量、使用进程池、仔细考虑IPC和使用异常处理。
    Python使用multiprocessing如何实现多进程
    2024-04-02

    shell的多进程实现

    方法总结: 1. command & + wait 方式 2. 管道fifo 1.1 实例1.1:将需要多进程执行的程序块全部使用command & wait 转移到后台执行即可
    2023-06-06

    node如何实现定时任务

    这篇文章主要介绍“node如何实现定时任务”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“node如何实现定时任务”文章能帮助大家解决问题。node实现定时任务的方法:1、利用setTimeOut和e
    2023-07-04

    Node服务如何进行Docker镜像化

    本文小编为大家详细介绍“Node服务如何进行Docker镜像化”,内容详细,步骤清晰,细节处理妥当,希望这篇“Node服务如何进行Docker镜像化”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。以一个例子开头,大
    2023-07-04

    如何自定义node版本,实现node多版本控制方式

    这篇文章主要介绍了如何自定义node版本,实现node多版本控制方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-16

    Nodejs搭建多进程Web服务器实现过程

    这篇文章主要为大家介绍了Nodejs搭建多进程Web服务器实现过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-13

    如何使用 C++ 函数实现多进程编程?

    c++++ 中的多进程编程涉及使用 头文件创建和管理并行运行的进程。创建进程需要使用 std::thread 构造函数,并向其传递一个要运行的函数。参数可以通过构造函数作为附加参数传递。一个实战案例演示了使用多进程计算大数字的分解。使用
    如何使用 C++ 函数实现多进程编程?
    2024-04-26

    Linux网络编程如何使用多进程实现服务器并发访问

    Linux网络编程如何使用多进程实现服务器并发访问,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。采用多进程的方式实现服务器的并发访问的经典范例。程序实现功能:1
    2023-06-13

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录