node ~ 进程

node ~ 进程

技术杂谈小彩虹2021-08-16 3:07:20220A+A-

进程

是计算机调度任务和分配任务的基本单位,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)

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们