浅谈Node.js 子进程与应用场景
背景
由于ons(阿里云RocketMQ包)基于C艹封装而来,不支持单一进程内实例化多个生产者与消费者,为了解决这一问题,使用了Node.js子进程。
在使用的过程中碰到的坑
发布:进程管理关闭主进程后,子进程变为操作系统进程(pid为1)
几种解决方案
将子进程看做独立运行的进程,记录pid,发布时进程管理关闭主进程同时关闭子进程
主进程监听关闭事件,主动关闭从属于自己的子进程
子进程种类
- spawn:执行命令
- exec:执行命令(新建shell)
- execFile:执行文件
- fork:执行文件
子进程常用事件
- exit
- close
- error
- message
close与exit是有区别的,close是在数据流关闭时触发的事件,exit是在子进程退出时触发的事件。因为多个子进程可以共享同一个数据流,所以当某个子进程exit时不一定会触发close事件,因为这个时候还存在其他子进程在使用数据流。
子进程数据流
- stdin
- stdout
- stderr
因为是以主进程为出发点,所以子进程的数据流与常规理解的数据流方向相反,stdin:写入流,stdout、stderr:读取流。
spawn
spawn(command[,args][,options])
执行一条命令,通过data数据流返回各种执行结果。
基础使用
const{spawn}=require('child_process'); constchild=spawn('find',['.','-type','f']); child.stdout.on('data',(data)=>{ console.log(`childstdout:\n${data}`); }); child.stderr.on('data',(data)=>{ console.error(`childstderr:\n${data}`); }); child.on('exit',(code,signal)=>{ console.log(`childprocessexitwith: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.-typef|wc-l',(err,stdout,stderr)=>{ if(err){ console.error(`execerror:${err}`); return; } console.log(`Numberoffiles${stdout}`); });
两全其美——spawn代替exec
由于exec的结果是一次性返回,在返回前是缓存在内存中的,所以在执行的shell命令输出过大时,使用exec执行命令的方式就无法按期望完成我们的工作,这个时候可以使用spawn代替exec执行shell命令。
const{spawn}=require('child_process'); constchild=spawn('find.-typef|wc-l',{ stdio:'inherit', shell:true }); child.stdout.on('data',(data)=>{ console.log(`childstdout:\n${data}`); }); child.stderr.on('data',(data)=>{ console.error(`childstderr:\n${data}`); }); child.on('exit',(code,signal)=>{ console.log(`childprocessexitwith: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'); constchild=fork('child.js'); child.on('message',(msg)=>{ console.log('Messagefromchild',msg); }); child.send({hello:'world'});
//child.js process.on('message',(msg)=>{ console.log('Messagefromparent:',msg); }); letcounter=0; setInterval(()=>{ process.send({counter:counter++}); },3000);
fork实际是spawn的一种特殊形式,固定spawnNode.js进程,并且在主子进程间建立了通信通道,让主子进程可以使用process模块基于事件进行通信。
子进程使用场景
- 计算密集型系统
- 前端构建工具利用多核CPU并行计算,提升构建效率
- 进程管理工具,如:PM2中部分功能
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。