背景
由于ons(阿里云 RocketMQ 包)基于 C艹 封装而来,不支持单一进程内实例化多个生产者与消费者,为了解决这一问题,使用了 Node.js 子进程。
在使用的过程中碰到的坑
发布:进程管理关闭主进程后,子进程变为操作系统进程(pid 为 1)
几种解决方案
将子进程看做独立运行的进程,记录 pid,发布时进程管理关闭主进程同时关闭子进程
子进程种类
子进程常用事件
- exit
- close
- error
- message
close 与 exit 是有区别的,close 是在数据流关闭时触发的事件,exit 是在子进程退出时触发的事件。因为多个子进程可以共享同一个数据流,所以当某个子进程 exit 时不一定会触发 close 事件,因为这个时候还存在其他子进程在使用数据流。
子进程数据流
- stdin
- stdout
- stderr
因为是以主进程为出发点,所以子进程的数据流与常规理解的数据流方向相反,stdin:写入流,stdout、stderr:读取流。
spawn
执行一条命令,通过 data 数据流返回各种执行结果。
基础使用
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}
);
});
常用参数
重点说明下 detached 属性,detached 设置为 true 是为子进程独立运行做准备。子进程的具体行为与操作系统相关,不同系统表现不同,Windows 系统子进程会拥有自己的控制台窗口,POSIX 系统子进程会成为新进程组与会话负责人。
这个时候子进程还没有完全独立,子进程的运行结果会展示在主进程设置的数据流上,并且主进程退出会影响子进程运行。当 stdio 设置为 ignore 并调用 child.unref(); 子进程开始真正独立运行,主进程可独立退出。
exec
执行一条命令,通过回调参数返回结果,指令未执行完时会缓存部分结果到系统内存。
if (err) {
console.error(
exec error: ${err}
);return;
}
console.log(Number of files ${stdout}
);
});
两全其美 —— spawn 代替 exec
由于 exec 的结果是一次性返回,在返回前是缓存在内存中的,所以在执行的 shell 命令输出过大时,使用 exec 执行命令的方式就无法按期望完成我们的工作,这个时候可以使用 spawn 代替 exec 执行 shell 命令。
stdio: 'inherit',shell: true
});
child.stdout.on('data',signal: ${signal}`);
});
execFile
执行一个文件
与 exec 功能基本相同,不同之处在于执行给定路径的一个脚本文件,并且是直接创建一个新的进程,而不是创建一个 shell 环境再去运行脚本,相对更轻量级更高效。但是在 Windows 系统中如 .cmd 、 .bat 等文件无法直接运行,这是 execFile 就无法工作,可以使用 spawn、exec 代替。
fork
执行一个 Node.js 文件
const child = fork('child.js');
child.on('message',(msg) => {
console.log('Message from child',msg);
});
child.send({ hello: 'world' });
console.log('Message from parent:',msg);
});
let counter = 0;
setInterval(() => {
process.send({ counter: counter++ });
},3000);
fork 实际是 spawn 的一种特殊形式,固定 spawn Node.js 进程,并且在主子进程间建立了通信通道,让主子进程可以使用 process 模块基于事件进行通信。