进程
是计算机调度任务和分配任务的基本单位,node不能实现多线程,但是可以开子进程,有个自带模块 child_process,可以为我们创建一个进程服务,不会影响node事件环
多进程
一个进程占用一个CPU,可以通过如下查看电脑cpu内核
let os = require('os').cpus().length;
console.log(os)
这里还有集群的概念,在cpu密集情况下,node并不太适合复杂的逻辑运算,此时我们要放给另一个进程来做,这时候我们就需要进程之间通信来获取数据
cpu1个cpu可以起很多个进程,一个服务会占用一个进程,一个进程是挂在cpu上的
希望我们的服务是多进程的,多个进程可以占用多个cpu
- 主进程 守护进程
- 子进程 工作进程
- 一个服务可以开启多个进程 (负载均衡)
再node中 一个进程包含一个主线程
集群
我们可以不是雾分成很多分,一般根据cpu分,node一般一个进程里面又一个线程
可能我们会开个进程负责爬,另一个进程负责写页面,那么如何创建一个子进程,并且让进程之间通信
// 进程有n个方法 spawn 产卵 fork 叉子 exec 执行 execFile 执行文件 后面都是基于spawn
// 只有主进程可以开多个子进程
let { spawn } = require('child_process');
let path = require('path');
// spawn 产卵 生小进程
// 进程之间默认数据是不能互相通信的
let child = spawn('node',['1.test.js','a','b','c'],{ //执行node命令
cwd:path.join(__dirname,'test'), //获取当前工作目录
// p主进程三个 process.stdin 0 process.stdout 1 process.stderr 2
stdio:[0,1,2]// 不写默认管道类型,pipe将父进程的这三个属性传递给了子进程(共用)
});
//进程通信
child.stdout.on('data',function(data){
console.log(data.toString());
})
child.on('error',function (err) {
console.log('err');
});
child.on('exit',function () {
console.log('exit')
});
进程通信
//1.test.js
process.argv.forEach(element => {
process.stdout.write(element);
});
需求
- 建立三个进程
- 主进程负责创建两个进程
- 将第一个进程的参数传入第二个进程
- 第二个进程写入到文件
//1.js
let { spawn } = require('child_process');
let path = require('path');
//开两个子进程
let child1 = spawn('node',['1.test.js','a','b','c'],{ //执行node命令 执行那个文件和参数
cwd: path.join(__dirname,'..','pro') //获取当前工作目录
});
let child2 = spawn('node',['2.test.js','a','b','c'],{ //执行node命令 执行那个文件和参数
cwd: path.join(__dirname,'..','pro') //获取当前工作目录
});//异步
child1.stdout.on('data',function(data){
//获取进程1返回的数据发送给进程2
child2.stdout.write(data)
})
//1.test.js
process.argv.slice(2).forEach(arg => {
process.stdout.write(arg);
});
//2.test.js
let fs = require('fs');
let ws = fs.createWriteStream('./1.txt');
process.stdout.on('data',function(data){
ws.write(data);
})
setTimeout(function(){
process.exit(); //退出进程,主进程执行完毕也会退出进程
},1000)
上术方法我们需要不停的on还需要考虑是stdin还是stdout,我们可以创建进程间的通道
进程通信
//2.js fork的方式
let {spawn} = require('child_process');
let path = require('path');
let child = spawn('node',['ipc.js'],{
cwd: path.join(__dirname,'..','pro') ,
stdio:['pipe',1,'pipe','ipc']
//ignore 不要子进程数据 ,pipe管道 ,null ,可以加ipc通信
});
child.send('我美吗');
child.on('message',function (data) {
console.log(data);
child.kill();//杀进程
});
//ipc.js
process.on('message',function (data) {
process.send(data + ' 你很美')
});
主进程控制子进程,但是如果主进程挂了,子进程也会挂,我们在执行的时候放弃控制权,如下
let { spawn } = require('child_process');
let path = require('path');
let fd = require('fs').openSync('./100.txt','w');
// 独立进程 stdio要设置成和父进程没有关系即可
let child = spawn('node', ['ipc.js'], {
cwd: path.join(__dirname,'..','pro'),
stdio: ['ignore', fd , 'ignore'],
detached:true //准备放弃
});
child.unref(); // 表示父进程挂了 儿子还会继续允许
//ipc.js
setInterval(function(){
process.stdout.write('hello')
},3000)
fork
fork用法基本一致
let { fork } = require('child_process');
let path = require('path');
let child = fork('ipc.js', ['a', 'b'], { //默认node不用在写node
cwd: path.join(__dirname,'..','pro'),
silent: false // 安静的,将stdio[ignore*3,'ipc']
})
//默认ipc方式,可以直接send message
child.send('hello');
child.on('message', function (data) {
console.log(data);
})
用spawn实现fork
//执行模块,参数,对象
function fork(modulePath, args, options = {}) {
if (options.silent) {
options.stdio = ['ignore', 'ignore', 'ingore', 'ipc']
} else {
options.stdio = [0, 1, 2, 'ipc']
}
return spawn('node', [modulePath, ...args], {
...options
})
}
集群
集群只是进程的简化方式,一个进程可以开一个线程,我们想用两个进程监听一个服务,
//3.js 主进程
let http = require('http');
let { fork } = require('child_process');
let path = require('path');
let child = fork('http.js', {
cwd: path.join(__dirname,'..','pro')
})
let server = http.createServer(function(res,req){
res.setEncoding('父进程处理请求');
}).listen(3000)
child.send('server',server);//send的第二个参数只能放 http的服务或者tcp的服务
//子进程 http.js
let http = require('http');
//子进程帮忙处理父进程的请求 不止监听端口号,监听另一个服务
process.on('message', function (msg, server) {
http.createServer(function(res,req){
res.setEncoding('子进程处理请求');
}).listen(server)
})
//开启服务cilent.js
let http = require('http');
for(var i = 0;i<1000;i++){
http.get({
port: '3000',
hostname: 'localhost'
},(res)=>{
res.on('data',function (data) {
console.log(data.toString());
})
})
}
上述这样写容易挂,我们可以用net
//3.js 主进程
let net = require('net');
let { fork } = require('child_process');
let path = require('path');
let child = fork('socket.js', {
cwd: path.join(__dirname,'..','pro')
})
let server = http.createServer(function(socket){
if(Math.random > 0.5){
socket.write('father')
}else{
child.send('socket',socket)
}
}).listen(3000)
//子进程 socket.js
let http = require('http');
//子进程帮忙处理父进程的请求 不止监听端口号,监听另一个服务
process.on('message', function (msg, server) {
if(masg == 'socket'){
socket.write('child')
}
})
//开启服务
用我们之前说的putty
execFile 执行命令/文件 exec 执行命令
- spawn是异步的,接收数据需要on send on message
- execFile 可以等待结果一起输出
- 不支持参数,只执行命令,实际也可以执行文件
// let {execFile} = require('child_process');
// execFile('node',['--version'],function (err,stdout,stderr) {
// console.log(stdout);
// })
// execFile('ls',['-1'],function (err,stdout,stderr) {//展示列表,详细的
// console.log(stdout);
// })
let {exec} = require('child_process');
// webpack --open
// 执行一些命令
exec('start http://localhost:3000', function (err, stdout, stderr) {
console.log(stdout);
});
集群 cluster
进程并非越多越好,根据cpu的核数
let {cluster} = require('child_process');
//可以通过ipc进行通信,想用管道setUpMaster
if(cluster.isMaster){
//在主分支中创建进程,返回的是进程
let worker = cluster.fork();//创建完后会再发一个命令执行当前文件
console.log('父进程')
}else{
console.log('子进程')
process.exit();
process.disconnect();
}
cluster.on('disconnect',function(){ //监听fork事件
console.log(disconnect)
})
cluster.on('fork',function(worker){ //监听fork事件
console.log(worker.id)
})
cluster.on('exit',function(){ //监听fork事件
console.log(exit)
})
根据cpu开进程
let {cluster} = require('child_process');
let cpus = require('os').cpus().length;
let http = require('http')
let path = require('path')
//可以通过ipc进行通信,想用管道setUpMaster
if(cluster.isMaster){
console.log('父进程')
for(let i=0;i<cpus.length;i++){
cluster.fork();
}
}else{
console.log('子进程')
http.createServer(function(res,req){
res.end('ok' + process.pid)
}).listen(3000)
}
setUpMaster
let {cluster} = require('child_process');
let cpus = require('os').cpus().length;
let http = require('http')
let path = require('path')
//可以通过ipc进行通信,想用setUpMaster
if(cluster.isMaster){
cluster.setUpMaster({
stdio:'pipe',//管道通信
exec:path.join(__dirname,'subprocess.js') //和子进程分开写
})
console.log('父进程')
for(let i=0;i<cpus.length;i++){
cluster.fork();
}
}
cluster.on('fork',function(worker){ //监听fork事件
console.log(worker.id)
})
//subprocess.js
let http = require('http')
console.log('子进程')
http.createServer(function(res,req){
res.end('ok' + process.pid)
}).listen(3000)