nodejs事件循环与多进程(四)——Process进程-Node全局对象&child_process子进程-exec、execSync、execFile、spawn、fork & Cluster集群
cluster相关API
Process 进程 、child_process 子进程 、Cluster 集群
process进程
process 对象是 Node 的一个全局对象,提供当前 Node 进程的信息,他可以在脚本的任意位置使用,不必通过 require 命令加载。
属性
- process.argv 属性,返回一个数组,包含了启动 node 进程时的命令行参数
- process.env 返回包含用户环境信息的对象,可以在 脚本中对这个对象进行增删改查的操作
- process.pid 返回当前进程的进程号
- process.platform 返回当前的操作系统
- process.version 返回当前 node 版本
方法
- process.cwd() 返回 node.js 进程当前工作目录
- process.chdir() 变更 node.js 进程的工作目录
- process.nextTick(fn) 将任务放到当前事件循环的尾部,添加到 ‘next tick’ 队列,一旦当前事件轮询队列的任务全部完成,在 next tick 队列中的所有 callback 会被依次调用
- process.exit() 退出当前进程,很多时候是不需要的
- process.kill(pid[,signal]) 给指定进程发送信号,包括但不限于结束进程
事件
-
beforeExit 事件,在 Node 清空了 EventLoop 之后,再没有任何待处理任务时触发,可以在这里再部署一些任务,使得 Node 进程不退出,显示的终止程序时(process.exit()),不会触发
-
exit 事件,当前进程退出时触发,回调函数中只允许同步操作,因为执行完回调后,进程金辉退出
-
uncaughtException 事件,当前进程抛出一个没有捕获的错误时触发,可以用它在进程结束前进行一些已分配资源的同步清理操作,尝试用它来恢复应用的正常运行的操作是不安全的
重点关注
-
warning 事件,任何 Node.js 发出的进程警告,都会触发此事件
child_process
nodejs中用于创建子进程的模块,node中大名鼎鼎的cluster是基于它来封装的。
- exec()
异步衍生出一个 shell,然后在 shell 中执行命令,且缓冲任何产生的输出,运行结束后调用回调函数
var exec = require('child_process').exec;
var ls = exec('ls -c', function (error, stdout, stderr) {
if (error) { // 错误处理和 业务代码一样重要
console.log(error.stack);
console.log('Error code: ' + error.code);
}
console.log('Child Process STDOUT: ' + stdout);
});
由于标准输出和标准错误都是流对象(stream),可以监听data事件,因此上面的代码也可以写成下面这样。
var exec = require('child_process').exec;
var child = exec('ls');
child.stdout.on('data', function(data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function(data) {
console.log('stdout: ' + data);
});
child.on('close', function(code) {
console.log('closing code: ' + code);
});
上面的代码还有一个好处。监听data事件以后,可以实时输出结果,否则只有等到子进程结束,才会输出结果。所以,如果子进程运行时间较长,或者是持续运行,第二种写法更好。
实例
var exec = require('child_process').exec;
// 回调
// exec('ls', (err, stdout, stderr) => {
// if (err) { // 错误处理和 业务代码一样重要
// console.log('stderr', stderr)
// }
// console.log('stdout', stdout)
// })
// 通过流的方式去接收结果,类似文件读取,
// rm -rf 删除
var path = '../ \ rm -rf';
var child = exec(`ls -c ${path}`);
child.stdout.on('data', (data) => {
console.log('data', data)
});
child.stderr.on('data', (err) => {
// 打印err
})
console.log('11111');
- execSync()
exec()的同步版本
- execFile()
execFile方法直接执行特定的程序shell,参数作为数组传入,不会被bash解释,因此具有较高的安全性。
const {execFile} = require('child_process');
execFile('ls',['-c'], (error, stdout, stderr) => { // execFile会自动过滤一些敏感的 字符串 比如'\ ;'
console.log('stdout', stdout)
if(error) {
console.error(`exec error: ${error}`);
return;
}
console.log(`${stdout}`);
console.log(`${stderr}`);
});
- spawn()
spawn方法创建一个子进程来执行特定命令shell,用法与execFile方法类似,但是没有回调函数,只能通过监听事件,来获取运行结果。它属于异步执行,适用于子进程长时间运行的情况。
const { spawn } = require('child_process');
var child = spawn('ls', ['-c'],{
encoding: 'UTF-8'
});
child.stdout.on('data', function(data) { // spawn的data输出的是buffer对象
console.log('data', data.toString('utf8'))
});
child.on('close',function(code) {
console.log('closing code: ' + code);
});
spawn返回的结果是Buffer需要转换为utf8
- fork()
fork方法直接创建一个子进程,执行Node脚本,fork('./child.js')
相当于 spawn('node', ['./child.js'])
。与spawn方法不同的是,fork会在父进程与子进程之间,建立一个通信管道pipe,用于进程之间的通信,也是IPC通信的基础。
main.js
var child_process = require('child_process');
var path = require('path');
// mian里面去创建一个子进程 child
var child = child_process.fork(path.resolve(__dirname, './child.js'));// 稳定性
child.on('message', function(m) { // main去监听child的消息
console.log('主线程收到消息', m);
// console.log('父亲接收到数据', data);
});
child.send({ hello: 'world' });
// child.send('儿子,爸爸给你发消息了');
child.js
process.on('message', function (m) {
console.log('子进程收到消息', m);
// console.log('儿子接收到的消息', data)
});
// 给父亲发送
process.send({ foo: 'bar' });
// process.send('爸爸你好');
打开终端,执行命令
node .\main.js
两个文件的console内容都能打印