创建子进程的几种方式
1、child_process.exec(command[, options][, callback])
2、child_process.execFile(file[, args][, options][, callback])
3、child_process.fork(modulePath[, args][, options])
4、child_process.spawn(command[, args][, options])
其中exec,execFile,fork均是从spawn衍生出来。
通道的建立和原理
stdout,stdin,stderr是共享了父进程的文件操作符,所以我们可以通过创建文件操作符来进行进程间共享,创建方法是fs.open或者fs.openSync
这四种的区别(只关注重点区别)
1、child_process.exec(command[, options][, callback])
特点:
(1)衍生一个shell,然后在shell执行命令。相当于把command直接发送给shell执行。
(2)具有回调函数子进程结束或者意外退出会调用回调方法
(3)具有maxbuffer,stdout 或 stderr 上允许的最大字节数。如果超过限制,则子进程将终止。默认值: 200 * 1024。也因为如此,所以exec启用的子进程不适合与父进程发送大量数据。
(4)timeout,设置子进程的运行时间,如果大于0,子进程会在设定的时间过后接收到父进程发送的带 killSignal 属性(默认为 'SIGTERM')的信号。
2、child_process.execFile(file[, args][, options][, callback])
特点:
(1)用于执行可执行文件
(2)不衍生shell,使用可执行 file 直接作为新进程衍生,使其比 child_process.exec() 稍微更高效。
(3)具有maxbuffer,stdout 或 stderr 上允许的最大字节数。如果超过限制,则子进程将终止。默认值: 200 * 1024。也因为如此,所以exec启用的子进程不适合与父进程发送大量数据。
(4)timeout,设置子进程的运行时间,如果大于0,子进程会在设定的时间过后接收到父进程发送的带 killSignal 属性(默认为 'SIGTERM')的信号。
(5)由于没有生成 shell,因此不支持 I/O 重定向和文件通配等行为。
3、child_process.fork(modulePath[, args][, options])
特点:
(1)是 child_process.spawn() 的一个特例,专门用于衍生新的 Node.js 进程。
(2)与 child_process.spawn() 一样返回 ChildProcess 对象。 返回的 ChildProcess 会内置一个额外的通信通道,允许消息在父进程和子进程之间来回传递。
(3)shell 选项在 child_process.fork() 中不支持,如果设置则会被忽略。
(4)可以设置stdio,但是必须包含一个额外的ipc通道
(5)可以在配置中指定execPath,这样便可以使用不同的node版本启动进程了
4、child_process.spawn(command[, args][, options])
特点:
(1)以上三个方法有的功能均能通过这个方法实现
实现功能
1、child_process.spawn(command[, args][, options])
这个方法能实现的功能比较多。
1、实现子进程通过stdio配置往一个文件中写内容
//主进程
const {spawn}=require('child_process');
const fs=require('fs');
//获取文件描述符
let fd=fs.openSync('./a.temp','r+');
let child1=spawn('node',['./child1.js'],{
stdio:['pipe',fd,'pipe']
});
//子进程
process.stdin.on('data',(data)=>{
console.log(data)
})
setInterval(()=>{
process.stdout.write('哈哈哈','utf-8')
},20)
2、指定环境变量
//主进程
const {exec}=require('child_process');
let child=exec('node ./child.js',{
env:{
mode:'dev'
}
},(err,stdout,stderr)=>{
console.log(stdout)
})
//child
process.stdout.write(process.env.mode)
//输出dev
3、父子进程解除引用
解除引用大的前提有以下几点:
(1)、父子进程之间不能共享stdio,ipc。
(2)、detached:true,需要配置此项。
//让父进程脱离子进程配置
let child= spawn('node',['./child.js'],{detached:true,stdio:"ignore"})
4、设置编码
以下演示设置编码的两种方式。
const {spawn,exec}=require('child_process');
//使用spawn的编码方式
let childSpawn=spawn('node',['-v']);
childSpawn.stdout.setEncoding('utf8');
childSpawn.stdout.on('data',(data)=>{
console.log(data)
})
//这种方式显而更简单
let childExec=exec('node -v',{encoding:'utf8'},(err,stdout,stdin)=>{
})
childExec.stdout.on('data',(data)=>{
console.log(data)
})
5、设置maxBuffer
maxBuffer的含义是接受到的字节总数,这个的作用是可以防止父进程不处理数据子进程一直写入导致内存爆掉。
//主进程
const {spawn,exec}=require('child_process');
let maxBufferLength=1024*1200;
let childSpawn=spawn('node',['./child.js']);
let sum=0;
childSpawn.stdout.on('data',(data)=>{
sum=sum+Buffer.byteLength(data);
if(sum>=maxBufferLength){
childSpawn.kill('SIGTERM')
}
})
childSpawn.on('exit',(code,sig)=>{
console.log(sig)
})
//使用exec限流
let childExec=exec('node ./child.js',{maxBuffer:maxBufferLength},(err,stdout,stderr)=>{
console.log(err)
})
childExec.on('exit',(code,sig)=>{
console.log(sig)
})
//child.js
setInterval(()=>{
process.stdout.write(Buffer.alloc(1024*400,"hha"))
},1000)
6、设置timeOut
设置timeOut
const {spawn,exec}=require('child_process');
let timeOut=3000;
let childSpawn=spawn('node',['./child.js']);
childSpawn.stdout.on('data',(data)=>{
})
setTimeout(()=>{
childSpawn.kill('SIGTERM')
},timeOut);
childSpawn.on('exit',(code,sig)=>[
console.log(sig)
])
//使用exec设置超时
let childExec=exec('node ./child.js',{timeout:timeOut},(err,stdout,stderr)=>{
console.log(err)
})
childExec.on('exit',(code,sig)=>[
console.log(sig)
])
//child.js
setInterval(()=>{
process.stdout.write(Buffer.alloc(1024*400,"hha"))
},1000)