NodeJS能够感知和控制自身进程的运行环境和状态,也能够建立子进程并与其协同工做,这使得NodeJS能够把多个程序组合在一块儿共同完成某项工做,并在其中充当胶水和调度器的做用。html
咱们已经知道了NodeJS自带的fs
模块比较基础,把一个目录里的全部文件和子目录都拷贝到另外一个目录里须要写很多代码。另外咱们也知道,终端下的cp
命令比较好用,一条cp -r source/* target
命令就能搞定目录拷贝。那咱们首先看看如何使用NodeJS调用终端命令来简化目录拷贝,示例代码以下:node
var child_process = require('child_process'); var util = require('util'); function copy(source, target, callback) { child_process.exec( util.format('cp -r %s/* %s', source, target), callback); } copy('a', 'b', function (err) { // ...
});
从以上代码中能够看到,子进程是异步运行的,经过回调函数返回执行结果。api
1、API数组
一、process:官方文档: http://nodejs.org/api/process.html安全
任何一个进程都有启动进程时使用的命令行参数,有标准输入标准输出,有运行权限,有运行环境和运行状态。服务器
在NodeJS中,能够经过process
对象感知和控制NodeJS自身进程的方方面面。app
另外须要注意的是,process
不是内置模块,而是一个全局对象,所以在任何地方均可以直接使用。异步
二、Child Process:官方文档: http://nodejs.org/api/child_process.html函数
使用child_process
模块能够建立和控制子进程。ui
该模块提供的API中最核心的是.spawn
,其他API都是针对特定使用场景对它的进一步封装,算是一种语法糖。
三、Cluster:官方文档: http://nodejs.org/api/cluster.html
cluster
模块是对child_process
模块的进一步封装,专用于解决单进程NodeJS Web服务器没法充分利用多核CPU的问题。使用该模块能够简化多进程服务器程序的开发,让每一个核上运行一个工做进程,并统一经过主进程监听端口和分发请求。
2、应用场景
一、如何获取命令行参数
在NodeJS中能够经过process.argv
获取命令行参数。
可是比较意外的是,node
执行程序路径和主模块文件路径固定占据了argv[0]
和argv[1]
两个位置,而第一个命令行参数从argv[2]
开始。为了让argv
使用起来更加天然,能够按照如下方式处理。
function main(argv) { // ...
} main(process.argv.slice(2));
二、如何退出程序
一般一个程序作完全部事情后就正常退出了,这时程序的退出状态码为0
。或者一个程序运行时发生了异常后就挂了,这时程序的退出状态码不等于0
。若是咱们在代码中捕获了某个异常,可是以为程序不该该继续运行下去,须要当即退出,而且须要把退出状态码设置为指定数字,好比1
,就能够按照如下方式:
try { // ...
} catch (err) { // ...
process.exit(1); }
三、如何控制输入输出
NodeJS程序的标准输入流(stdin)、一个标准输出流(stdout)、一个标准错误流(stderr)分别对应process.stdin
、process.stdout
和process.stderr
,第一个是只读数据流,后边两个是只写数据流,对它们的操做按照对数据流的操做方式便可。例如,console.log
能够按照如下方式实现。
function log() { process.stdout.write( util.format.apply(util, arguments) + '\n'); }
四、如何降权
在Linux系统下,咱们知道须要使用root权限才能监听1024如下端口。可是一旦完成端口监听后,继续让程序运行在root权限下存在安全隐患,所以最好能把权限降下来。如下是这样一个例子。
http.createServer(callback).listen(80, function () { var env = process.env, uid = parseInt(env['SUDO_UID'] || process.getuid(), 10), gid = parseInt(env['SUDO_GID'] || process.getgid(), 10); process.setgid(gid); process.setuid(uid); });
上例中有几点须要注意:
若是是经过sudo
获取root权限的,运行程序的用户的UID和GID保存在环境变量SUDO_UID
和SUDO_GID
里边。若是是经过chmod +s
方式获取root权限的,运行程序的用户的UID和GID可直接经过process.getuid
和process.getgid
方法获取。
process.setuid
和process.setgid
方法只接受number
类型的参数。
降权时必须先降GID再降UID,不然顺序反过来的话就没权限更改程序的GID了。
五、如何建立子进程
如下是一个建立NodeJS子进程的例子。
var child = child_process.spawn('node', [ 'xxx.js' ]); child.stdout.on('data', function (data) { console.log('stdout: ' + data); }); child.stderr.on('data', function (data) { console.log('stderr: ' + data); }); child.on('close', function (code) { console.log('child process exited with code ' + code); });
上例中使用了.spawn(exec, args, options)
方法,该方法支持三个参数。第一个参数是执行文件路径,能够是执行文件的相对或绝对路径,也能够是根据PATH环境变量能找到的执行文件名。第二个参数中,数组中的每一个成员都按顺序对应一个命令行参数。第三个参数可选,用于配置子进程的执行环境与行为。
另外,上例中虽然经过子进程对象的.stdout
和.stderr
访问子进程的输出,但经过options.stdio
字段的不一样配置,能够将子进程的输入输出重定向到任何数据流上,或者让子进程共享父进程的标准输入输出流,或者直接忽略子进程的输入输出。
六、进程间如何通信
在Linux系统下,进程之间能够经过信号互相通讯。如下是一个例子。
/* parent.js */
var child = child_process.spawn('node', [ 'child.js' ]); child.kill('SIGTERM'); /* child.js */ process.on('SIGTERM', function () { cleanUp(); process.exit(0); });
在上例中,父进程经过.kill
方法向子进程发送SIGTERM
信号,子进程监听process
对象的SIGTERM
事件响应信号。不要被.kill
方法的名称迷惑了,该方法本质上是用来给进程发送信号的,进程收到信号后具体要作啥,彻底取决于信号的种类和进程自身的代码。
另外,若是父子进程都是NodeJS进程,就能够经过IPC(进程间通信)双向传递数据。如下是一个例子。
/* parent.js */
var child = child_process.spawn('node', [ 'child.js' ], { stdio: [ 0, 1, 2, 'ipc' ] }); child.on('message', function (msg) { console.log(msg); }); child.send({ hello: 'hello' }); /* child.js */ process.on('message', function (msg) { msg.hello = msg.hello.toUpperCase(); process.send(msg); });
能够看到,父进程在建立子进程时,在options.stdio
字段中经过ipc
开启了一条IPC通道,以后就能够监听子进程对象的message
事件接收来自子进程的消息,并经过.send
方法给子进程发送消息。在子进程这边,能够在process
对象上监听message
事件接收来自父进程的消息,并经过.send
方法向父进程发送消息。数据在传递过程当中,会先在发送端使用JSON.stringify
方法序列化,再在接收端使用JSON.parse
方法反序列化。
七、如何守护子进程
守护进程通常用于监控工做进程的运行状态,在工做进程不正常退出时重启工做进程,保障工做进程不间断运行。如下是一种实现方式。
/* daemon.js */ function spawn(mainModule) { var worker = child_process.spawn('node', [ mainModule ]); worker.on('exit', function (code) { if (code !== 0) { spawn(mainModule); } }); } spawn('worker.js');
能够看到,工做进程非正常退出时,守护进程当即重启工做进程。