众所周知,Node基于V8,JavaScript运行在V8中的单线程上。这里的单线程并不意味着Node启动时只有一个线程,而是JavaScript代码在单线程上运行,Node有其他线程,比如异步IO操作的IO线程。这种单线程模式的优点是系统调度过程中不会频繁发生上下文切换,提高了单核CPU的利用率。
但是这种方法有一个缺陷,就是我们不能利用服务器CPU的多核性能,一个Node进程只能利用一个CPU。一旦代码在单线程模式下崩溃,整个程序就会崩溃。通常的解决方案是使用Node的集群模块,通过主从模式启用多个流程实例。下面详细讲一下Node如何使用多进程模型和多核CPU,以及自身集群模块的具体工作原理。
const { spawn,exec,execFile,fork }=require(' child _ process ')spawn(command[,args][,options])exec(command[,options][,callback])execFile(file[,args][,options][,callback])fork(modulePath[,args][,options]]span)
首先,了解产卵方法。这是节点文档的正式示例。
const { spawn }=require(' child _ process ');const child=产卵(' ls ',['-lh ','/home ']);Child.on ('close ',(code)={console.log(`子进程退出代码:$[code]`));});const { stdin,stdout,stderr }=childstdout.on('data ',(data)={ console . log(` stdout : $ { data } `));});stderr.on('data ',(data)={ console . log(` stderr : $ { data } `));});由spawn创建的子进程继承自EventEmitter,因此它可以监视其上的事件(折扣、错误、关闭、消息)。同时,子进程有三个iostream: stdin、stdout和stderr。通过这三个流,可以实时获取子过程的输入输出和误差信息。
这个方法的最终实现是基于libuv的,所以这里就不讨论了。如果你感兴趣,可以查看源代码。
//调用libuv的api来初始化一个进程interr=uv _ spawn (env-event _ loop(),wrap-process _,options);exec/execFile
这两个放在一起的原因是execFile方法最终被exec调用。唯一不同的是,在exec中调用的normalizeExecArgs方法将默认将opts的shell属性设置为true。
exports . exec=function exec(/* command,options,callback */){ const opts=normalizexecargs . apply(null,arguments);return exports . exec file(opts . file,opts.options,opts . callback);};函数normalizeExecArgs(命令、选项、回调){ options={.选项};options . shell=type of options . shell===' string '?options.shell : true返回{ options };}在execFile中,最终调用了产卵方法。
exports . exec file=function exec file(file/*,args,options,callback */){ let args=[];让回调;让选项;var child=产卵(文件,参数,{ //.一些选项});返回孩子;}exec会将spawn的iostream转换为String,默认情况下会使用String