剑客
关注科技互联网

nodejs笔记4–多进程架构简介

填坑:

1. nodejs笔记1–异步API与其局限性

2. nodejs笔记2–nodejs中的事件通知机制

3. nodejs笔记3–事件通知机制的两个应用场景

4. nodejs笔记4–多进程架构

应用场景

操作系统的资源是有限的,这通常意味者操作系统分配给进程或线程的资源是有限的,在多核cpu普及的时代,如果只使用单核(常说node是单线程的),则无法充分压榨硬件性能.

模块介绍

node提供了child_process模块,该模块提供4个方法

1.spawn() 启动一子进程来执行命令

2.exec() 启动一子进程来执行命令,与spawn()不同的是提供一个回调函数获知子进程的状况

3.execFile() 启动一个子进程来执行可执行文件

4.fork() 与spawn()类似,指定执行的javascript文件模块即可

惯用做法

通常,node项目会有个bin目录,里面的文件类似于应用程序入口,结合 commander
包实现根据命令行参数执行相应代码逻辑的功能:

var child_process = require('child_process');  
var fs = require('fs');  
var os = require('os');  
var program = require('commander');

const EventEmitter = require('events').EventEmitter;  
const ee = new EventEmitter();

var config = require('../config.json');  
var DEFAULT_TRG_PATH = config.triggersPath;

program.command('start')  
  .description('start the application')
  .option('-d, --directory, <directory>', 'triggers path', DEFAULT_TRG_PATH)
  .action(function(opts) {
    start(opts);
  });

program.command('create')  
  .description('start create')
  .option('-d, --directory, <directory>', 'triggers path', DEFAULT_TRG_PATH)
  .action(function(opts) {
    create(opts);
  });

program.parse(process.argv);

function start(opts){  
  console.log(1111);
}
function create(opts){  
  console.log(1111);
}

假设需要执行 start 方式,仅需在命令行带上 start参数即可:

node ./bin/www start

同理,其他方法可以通过该方式按需调用. 都不到为啥会扯到commander包的使用,可能是通常情况下想使用多进程架构时功能已经足够复杂,而首先想到的就是用commander包使程序看上去比较好看吧-_-

好了,言归正传,其实我就想使用线程池这种所谓的高级语言封装好的方法而已,可惜node并没有这种.

很多文章都说过,API的用法,这里只简单说说他们之间的区别.

spawn

spawn会返回一个代后stdout与stderr流的对象,可以通过stdout流来读取子进程返回给主进程的数据.如果子进程需要返回大量数据,则应该使用spawn而不是exec. 注意,stdout是以buffer格式返回,相对于exec而言,buffer数据可以很大,也就是说如果主进程需要接收子进程大量的数据时,应该选择spawn来实现:

var absScript = path.resolve(process.cwd()+'/lib/', 'child.js');  
var child = child_process.spawn(process.argv[0],[absScript,'create','--path=' + dConfig.triggersPath + trgFile]);  
    child.stdout.on('data', function(data){
      console.log(`${data}`);
    });
    child.stdout.on('end', function(){
      console.log('end');
    });
    child.on('error', function(err){
      console.log('Failed to start child process.');
    });
    child.on('close',function(code){
      console.log('closecode:'+ code);
    });

//child.js
console.log('modelhunter.js');  
var fs = require('fs');  
var program = require('commander');  
var trgs = require('./components/trgs');  
var ModelHunter = module.exports = {};  
program.command('create')  
  .description('create')
  .option('-p, --path, <file>', 'triggers path', '')
  .action(function(opts) {
    if(opts.path.length>0){
      console.log(opts.path);
      setTimeout(function(){
        console.log('aaaaaaa');
        process.exit(0);
      },1000);
    }
  });
program.parse(process.argv);

最终输出为

modelhunter.js  
D:/xxxxx.xx  
aaaaaaa  
end  
closecode:0

exec 与 execFile

exec相当于spawn,通讯的内容有限制.适合执行复杂的shell命,与主进程的通讯是一次性的,子进程的结果只能返回一次.

execFile跟exec类型,执行一个文件,或脚本或可执行文件等

exec做了相当的封装,主要用于执行复杂的shell命令.

fork

fork使用同样的进程复制自身的环境执行脚本,但只能执行node脚本,fork可以很方便的与主进程进行通讯:

//注意这里第一个参数即是目标脚本的路径.
var absScript = path.resolve(process.cwd()+'/lib/', 'modelhunter.js');  
var child = child_process.fork(absScript,['create','--path=' + dConfig.triggersPath + trgFile]);  
    child.on('message',function(m){
      console.log('message:'+ m);    //接收子进程消息
    });
    child.on('close',function(code){
      console.log(code);
    });
    child.send('null');      //向子进程发送消息

//child.js
process.on('message',function(m){  
  console.log('chilid Listen:', m);  //接收主进程消息
});
process.send('this childprocess');   //向主进程发送消息

总结

如果想使用多进程架构,按需选择相应的API.即可实现类似"进程池"的功能,当然,可能要配合事件通知机制.

这里提出几点疑问:

1.如果是一个网站的服务端,为了充分利用CPU资源,通常会使用多进程架构,但是我们应该选择fork还是spawn呢?

2.pm2的fork mode 与 cluster mode有什么区别?

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址