浅谈Node.js 子进程与应用场景


Posted in Javascript onJanuary 24, 2018

背景

由于ons(阿里云 RocketMQ 包)基于 C艹 封装而来,不支持单一进程内实例化多个生产者与消费者,为了解决这一问题,使用了 Node.js 子进程。

在使用的过程中碰到的坑

发布:进程管理关闭主进程后,子进程变为操作系统进程(pid 为 1)

几种解决方案

将子进程看做独立运行的进程,记录 pid,发布时进程管理关闭主进程同时关闭子进程

主进程监听关闭事件,主动关闭从属于自己的子进程

子进程种类

  1. spawn:执行命令
  2. exec:执行命令(新建 shell)
  3. execFile:执行文件
  4. fork:执行文件

子进程常用事件

  1. exit
  2. close
  3. error
  4. message

close 与 exit 是有区别的,close 是在数据流关闭时触发的事件,exit 是在子进程退出时触发的事件。因为多个子进程可以共享同一个数据流,所以当某个子进程 exit 时不一定会触发 close 事件,因为这个时候还存在其他子进程在使用数据流。

子进程数据流

  1. stdin
  2. stdout
  3. stderr

因为是以主进程为出发点,所以子进程的数据流与常规理解的数据流方向相反,stdin:写入流,stdout、stderr:读取流。

spawn

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

执行一条命令,通过 data 数据流返回各种执行结果。

基础使用

const { spawn } = require('child_process');

const child = spawn('find', [ '.', '-type', 'f' ]);
child.stdout.on('data', (data) => {
  console.log(`child stdout:\n${data}`);
});

child.stderr.on('data', (data) => {
  console.error(`child stderr:\n${data}`);
});

child.on('exit', (code, signal) => {
  console.log(`child process exit with: code $[code], signal: ${signal}`);
});

常用参数

{
  cwd: String,
  env: Object,
  stdio: Array | String,
  detached: Boolean,
  shell: Boolean,
  uid: Number,
  gid: Number
}

重点说明下 detached 属性,detached 设置为 true 是为子进程独立运行做准备。子进程的具体行为与操作系统相关,不同系统表现不同,Windows 系统子进程会拥有自己的控制台窗口,POSIX 系统子进程会成为新进程组与会话负责人。

这个时候子进程还没有完全独立,子进程的运行结果会展示在主进程设置的数据流上,并且主进程退出会影响子进程运行。当 stdio 设置为 ignore 并调用 child.unref(); 子进程开始真正独立运行,主进程可独立退出。

exec

exec(command[, options][, callback])

执行一条命令,通过回调参数返回结果,指令未执行完时会缓存部分结果到系统内存。

const { exec } = require('child_process');

exec('find . -type f | wc -l', (err, stdout, stderr) => {
  if (err) {
    console.error(`exec error: ${err}`);
    return;
  }

  console.log(`Number of files ${stdout}`);
});

两全其美 —— spawn 代替 exec

由于 exec 的结果是一次性返回,在返回前是缓存在内存中的,所以在执行的 shell 命令输出过大时,使用 exec 执行命令的方式就无法按期望完成我们的工作,这个时候可以使用 spawn 代替 exec 执行 shell 命令。

const { spawn } = require('child_process');

const child = spawn('find . -type f | wc -l', {
  stdio: 'inherit',
  shell: true
});

child.stdout.on('data', (data) => {
  console.log(`child stdout:\n${data}`);
});

child.stderr.on('data', (data) => {
  console.error(`child stderr:\n${data}`);
});

child.on('exit', (code, signal) => {
  console.log(`child process exit with: code $[code], signal: ${signal}`);
});

execFile

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

执行一个文件

与 exec 功能基本相同,不同之处在于执行给定路径的一个脚本文件,并且是直接创建一个新的进程,而不是创建一个 shell 环境再去运行脚本,相对更轻量级更高效。但是在 Windows 系统中如 .cmd 、 .bat 等文件无法直接运行,这是 execFile 就无法工作,可以使用 spawn、exec 代替。

fork

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

执行一个 Node.js 文件

// parent.js

const { fork } = require('child_process');

const child = fork('child.js');

child.on('message', (msg) => {
  console.log('Message from child', msg);
});

child.send({ hello: 'world' });
// child.js

process.on('message', (msg) => {
  console.log('Message from parent:', msg);
});

let counter = 0;

setInterval(() => {
  process.send({ counter: counter++ });
}, 3000);

fork 实际是 spawn 的一种特殊形式,固定 spawn Node.js 进程,并且在主子进程间建立了通信通道,让主子进程可以使用 process 模块基于事件进行通信。

子进程使用场景

  1. 计算密集型系统
  2. 前端构建工具利用多核 CPU 并行计算,提升构建效率
  3. 进程管理工具,如:PM2 中部分功能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery EasyUI 中文API Layout(Tabs)
Apr 27 Javascript
jQuery ul标签下拉菜单演示代码
Dec 11 Javascript
JQuery之focus函数使用介绍
Aug 20 Javascript
jquery.gridrotator实现响应式图片展示画廊效果
Jun 23 Javascript
javascript顺序加载图片的方法
Jul 18 Javascript
14 个折磨人的 JavaScript 面试题
Aug 08 Javascript
Vue下拉框回显并默认选中随机问题
Sep 06 Javascript
JS实现的杨辉三角【帕斯卡三角形】算法示例
Feb 26 Javascript
JS数组Object.keys()方法的使用示例
Jun 05 Javascript
Vue+Element-UI实现上传图片并压缩
Nov 26 Javascript
原生JavaScript之es6中Class的用法分析
Feb 23 Javascript
jQuery实现手风琴特效
Jan 11 jQuery
除Console.log()外更多的Javascript调试命令
Jan 24 #Javascript
深入理解node.js http模块
Jan 24 #Javascript
微信、QQ、微博、Safari中使用js唤起App
Jan 24 #Javascript
基于node打包可执行文件工具_Pkg使用心得分享
Jan 24 #Javascript
Angular整合zTree的示例代码
Jan 24 #Javascript
使用classList来实现两个按钮样式的切换方法
Jan 24 #Javascript
基于vue.js 2.x的虚拟滚动条的示例代码
Jan 23 #Javascript
You might like
PHP syntax error, unexpected $end 错误的一种原因及解决
2008/10/25 PHP
Laravel 5.3 学习笔记之 安装
2016/08/28 PHP
PHP设计模式之工厂模式(Factory Pattern)的讲解
2019/03/21 PHP
PHP实现微信提现功能(微信商城)
2019/11/21 PHP
jQuery.extend 函数详解
2012/02/03 Javascript
查找iframe里元素的方法可传参
2013/09/11 Javascript
鼠标滑在标题上显示图片的JS代码
2013/11/19 Javascript
jqeury-easyui-layout问题解决方法
2014/03/24 Javascript
AngularJS中如何使用$http对MongoLab数据表进行增删改查
2016/01/23 Javascript
JS实现点击事件统计的简单实例
2016/07/10 Javascript
利用JS提交表单的几种方法和验证(必看篇)
2016/09/17 Javascript
Javascript实现一个简单的输入关键字添加标签效果实例
2017/06/01 Javascript
JavaScript简介_动力节点Java学院整理
2017/06/26 Javascript
详解用webpack把我们的业务模块分开打包的方法
2017/07/20 Javascript
详解JS中的this、apply、call、bind(经典面试题)
2017/09/19 Javascript
node下使用UglifyJS压缩合并JS文件的方法
2018/03/07 Javascript
详解React中传入组件的props改变时更新组件的几种实现方法
2018/09/13 Javascript
vue权限问题的完美解决方案
2019/05/08 Javascript
详细教你微信公众号正文页SVG交互开发技巧
2019/07/25 Javascript
javascript canvas API内容整理
2020/02/16 Javascript
浅析vue cli3 封装Svgicon组件正确姿势(推荐)
2020/04/27 Javascript
python网络编程学习笔记(五):socket的一些补充
2014/06/09 Python
Ubuntu18.04下python版本完美切换的解决方法
2019/06/14 Python
Python数据结构与算法(几种排序)小结
2019/06/22 Python
pymysql模块的使用(增删改查)详解
2019/09/09 Python
Python实现实时数据采集新型冠状病毒数据实例
2020/02/04 Python
python如何求数组连续最大和的示例代码
2020/02/04 Python
python使用正则表达式去除中文文本多余空格,保留英文之间空格方法详解
2020/02/11 Python
Django Serializer HiddenField隐藏字段实例
2020/03/31 Python
比驿:全球酒店比价网
2018/06/20 全球购物
美国潜水装备、水肺潜水和浮潜设备商店:Leisure Pro
2018/08/08 全球购物
李维斯牛仔裤荷兰官方网站:Levi’s NL
2020/08/23 全球购物
民政局个人整改措施
2014/09/24 职场文书
婚内分居协议书范文
2014/11/26 职场文书
优秀党员主要事迹范文
2015/11/05 职场文书
JavaGUI模仿QQ聊天功能完整版
2021/07/04 Java/Android