nodeJS - 8 - process进程 child_process子进程 Cluster 集群
Process 进程 、child_process 子进程 、Cluster 集群
虽然 Node.js 把许多东西从操作系统中抽象出来,但你依然在操作系统中运行,而且可能想要更直接的与他交互,Node 中可以使用系统中已经存在的进程,或者创建新的子进程来做各种工作,虽然 Node 本身是一个 ‘胖’ 线程,带有单独一个事件循环,但你可以任意的开启其他进程( 线程)在事件循环外工作 – 《Node 即学即用》
参考
1. Process 进程
说明
process 对象是 Node 的一个全局对象,提供当前 Node 进程的信息,他可以在脚本的任意位置使用,不必通过 require 命令加载。
属性 API
process.argv属性,返回一个数组,包含了启动 node 进程时的命令行参数process.env返回包含用户环境信息的对象,可以在 脚本中对这个对象进行增删改查的操作process.pid返回当前进程的进程号process.platform返回当前的操作系统process.version返回当前 node 版本process.execPath返回启动 Node.js 进程的可执行文件所在的绝对路径process.execArgv返回 在 Node 可执行文件与脚本文件之间的命令行参数process.stdin标准输入,实现Duplex流(可读、可写)process.stdout标准输出,实现Duplex流(可读、可写),write()方法相当于console.log()process.stderr标准错误
// # node index.js name test
console.log(process.argv);
/*
[
'/usr/local/bin/node', // process.execPath 启动 Node 进程的可执行文件所在的绝对路径
'/Users/xxx/node-test/index.js', // 当前执行的 javascript 文件路径
'name', // 其他命令行参数
'test'
]
*/
process.env.test = 'test'; // 增加
console.log(process.env.test); // test 查询
process.env.test = 'new'; // 改
console.log(process.env.test); // new
delete process.env.test; // 删除
console.log(process.env.test); // undefined
/*
新建环境变量 NODE_ENV,标注开发阶段还是生产阶段
命令行可以使用 export NODE_ENV=production & node index.js
*/
process.env.NODE_ENV = 'production'
console.log(process.pid); // 3105
console.log(process.platform); // darwin
console.log(process.version); // v8.9.0
console.log(process.execPath); // /usr/local/bin/node
// # node --harmony index.js
console.log(process.execArgv); // [ '--harmony' ]
let count = 0;
process.stdin.on('data', function (chunk) {
process.stdout.write(count++ + ' = ' + chunk);
})
process.stdin.on('end', function () {
process.stdout.write('end');
})
方法 API
process.cwd()返回 node.js 进程当前工作目录process.chdir()变更 node.js 进程的工作目录process.nextTick(fn)将任务放到当前事件循环的尾部,添加到 ‘next tick’ 队列,一旦当前事件轮询队列的任务全部完成,在 next tick 队列中的所有 callback 会被依次调用process.exit()退出当前进程,很多时候是不需要的process.kill(pid[,signal])给指定进程发送信号,包括但不限于结束进程process.emitWarning()发出特定或者定制的进程警告 可以通过process.on('warning)监听这些警告
console.log(process.cwd()); // /Users/temp/node-test
process.chdir('../');
console.log(process.cwd()); // / Users/temp
let count = 0;
process.on('warning', function (warning) {
console.log(warning);
})
process.stdin.on('data', function (chunk) {
process.nextTick(function () {
console.log('next tick');
})
process.stdout.write(count++ + '=' + chunk);
if (count > 3) {
process.emitWarning('something warning', {
code: 'WARNING_SOME',
detail: 'this is warning something'
})
process.exit(); // 退出进程
process.kill(process.pid); // 杀死进程
}
/*
ss
undefined undefined
*/
})
process 事件
beforeExit事件,在 Node 清空了 EventLoop 之后,再没有任何待处理任务时触发,可以在这里再部署一些任务,使得 Node 进程不退出,显示的终止程序时(process.exit()),不会触发exit事件,当前进程退出时触发,回调函数中只允许同步操作,因为执行完回调后,进程金辉退出uncaughtException事件,当前进程抛出一个没有捕获的错误时触发,可以用它在进程结束前进行一些已分配资源的同步清理操作,尝试用它来恢复应用的正常运行的操作是不安全的warning事件,任何 Node.js 发出的进程警告,都会触发此事件Signal Events信号事件,当 Node.js 进程接收到一个信号时触发
let count = 0;
process.on('beforeExit', function () {
console.log('beforeExit');
})
process.on('exit', function (code) {
console.log('exit', code);
})
process.on('uncaughtException', function (err) {
console.log('uncaughtException', err);
})
process.on('warning', function (warning) {
console.log('warning', warning);
})
process.on('SIGHUP', function () {
console.log('SIGHUP');
})
process.stdout.write('xxx');
process.kill(process.pid, 'SIGHUP')
/*
xxx SIGHUP
beforeExit
exit 0
*/
2. child_process 子进程
说明
child_process 模块用于新建子进程,子进程的运行结果存储在系统缓存中(最大200k),等到子进程运行结束后,主进程再调用回调函数读取子进程的运行结果
通过 fork() 等 API,创建子进程后,为了实现父子进程之间的通信,父子进程之间将会创建 IPC 通道,通过 IPC 通道父子进程之间才能通过 message 和 send() 传递消息
默认情况下,在 Node.js 的父进程与衍生进程之间会建立 stdin、stdout、stderr 的管道,数据能以非阻塞的方式在管道中流通
创建子进程
1. child_process.spawn() child_process.spwanSync()
spawn()会异步的衍生子进程,spawnSync()方法则以同步的方式提供同样的功能 ,但会阻塞事件循环、知道衍生的子进程退出或者终止参数
- 1)command: 所要执行的 shell 脚本命令
- 2)args: 字符串参数列表
- 3)options: 配置额外的选项:cwd: 子进程的当前工作目录;env:环境变量键值对…
// test.js 内容
process.stdout.write('this is child back');
// index.js 内容 执行 node index.js
const child_process = require('child_process');
const child = child_process.spawn('node', ['test.js'], {
cwd: './'
});
child.stdout.setEncoding('utf-8');
child.stdout.on('data', function (data) {
console.log('listen in process', data); // listen in process this is child back
})
const childSync = child_process.spawnSync('node', ['test.js'], {
cwd: './'
})
console.log(childSync); // pid stdout error ...
一下的接口都是为了方便起见,在 这两个接口(
spawn()、spawnSync())之上实现的
2. child_process.exec() child_process.execSync()
衍生出一个 shell,然后在 shell 中执行命令,且缓冲任何产生的输出,运行结束后调用回调函数
- 1)command: 要运行的命令,空格分割
- 2)options: 选填,cwd: 子进程的工作目录;env: 环境变量键值对
- 3)callback: 回调,会传入三个参数,error: 错误信息;stdout:标准输出显示结果;stdrr: 标准错误显示结果
const child_process = require('child_process');
child_process.exec('node test.js', function (error, stdout, stderr) {
if (error) {
return console.log(error);
}
console.log('stdout', stdout);
console.log('stderr', stderr);
})
const child = child_process.exec('node test.js');
child.stdout.on('data', function (data) {
console.log('listen ', data.toString());
})
const childSync = child_process.execSync('node test.js');
console.log(childSync.toString());
3. child_process.execFile() child_process.execFileSync()
类似 child_process.exec() 函数,除了不衍生一个 shell ,而是指定的可执行的 file 被直接衍生为一个新的进程,更加高效
- 1)file: 要运行的可执行文件的名称或者路径
- 2)args: 可选,字符串参数列表
- 3)options: 配置项 与 child_process.exec() 一致
- 4)callback: 回调 error 错误;stdout 标准输出;stderr 标准错误
const child_process = require('child_process');
child_process.execFile('node', ['test.js'], function (error, stdout, stderr) {
if (error) {
return console.log(error);
}
console.log('stdout', stdout);
console.log('stderr', stderr);
})
const childSync = child_process.execFileSync('node', ['test.js']);
console.log(childSync.toString());
4. child_process.fork() send()
fork() 用于直接创建一个子进程会返回一个 childProcess 对象,与spawn方法不同的是,fork 会在父进程与子进程间,建立一个通信管道,用于进程之间的通信
- 1)modulePath: 要在子进程中运行的模块
- 2)args: 字符串参数列表
- 3)option: 配置项 cwd: 子进程当前工作目录;env: 环境白变量;execpath: 用来创建子进程的执行路径
send() 用于向新进程发送消息,新进程中通过监听
message事件,来获取消息
const child_process = require('child_process');
const child = child_process.fork('./test.js');
// 监听 message 事件,获取子进程返回数据
child.on('message', function (m) {
console.log('process get', m);
})
child.send({parentID: process.pid});
5. 创建方法的比较
- spawn():启动一个子进程来执行命令
- exec():启动一个子进程来执行命令,并且通过回调函数通知子进程状况
- execFile():启动一个子进程来执行可执行文件
- fork():是 spawn() 的一个特例,创建子进程只需要指定要执行的 javascript 文件模块即可,子进程返回一个 child_process 对象,并附加父子进程间的通信渠道
- exec() 与 execFile() 创建时可以指定 timeout 属性设置超时时间,一旦创建的进程运行超过设定的时间将会被杀死
子进程对象的事件与属性
事件
close事件,当子进程的 ‘stdio’ 流被关闭时触发disconnect事件,在父进程中 subprocess.disconnect() 或者在子进程中调用 process.disconnect() 都会触发,断开后将关闭 IPC 通道,不能再发送或者接受消息error事件,当子进程无法被复制创建、无法被杀死、无法发送消息时会触发exit事件, 子进程结束后会触发,回调参数 code:退出码;signal: 被终止时的信号message事件,父、子进程间通信,接受信息用
属性
connected属性,表明是否仍可以从一个子进程发送和接收消息disconnect()方法,关闭父进程与子进程之间的 IPC 通道,一旦没有其他的连接使其保持活跃,则允许子进程正常退出。kill([signal])方法, 向子进程发送一个信号,不写参数默认结束子进程killed属性,表明该子进程已经成功地被 subprocess.kill() 终止pid属性,子进程的进程标识send方法,父进程和子进程之间建立了一个 IPC 通道时,用于发送信息
// 子进程 test.js
process.on('message', function (m) {
console.log('child process get ', m);
if (m === 0) process.send(3);
})
// 主进程
const child_process = require('child_process');
const child = child_process.fork('./test.js');
child.on('close', function (code, signal) {
console.log('close code', code);
console.log('close signal', signal);
})
child.on('exit', function (code, signal) {
console.log('exit code', code);
console.log('exit signal', signal);
})
child.on('disconnect', function () {
console.log('disconnect', child.connected);
})
child.on('error', function (err) {
console.log('error', err);
})
// 监听 message 事件,获取子进程返回数据
child.on('message', function (m) {
console.log('process get', m);
if (m === 1) child.disconnect();
if (m === 3) child.kill();
console.log(child.killed);
})
child.send(0);
3. Cluster 集群
说明
Node.js 默认单进程运行,对于多核 CPU 的计算机来说,这样做效率很低,因为只有一个核在运行,其他核都在闲置,面对单进程单线程对多核使用不足的问题,前人的经验是启动多进程。理想的状态下每个进程各自利用一个 CPU ,以此实现多核 CPU 的利用
Master-Worker模式:Cluster 模块允许设立一个主进程和若干个 worker 进程,由主进程监控和协调 worker 进程的运行,worker 之间采用进程间通信交换信息,cluster 模块内置一个负载均衡器,协调各个进程之间的负载。这是典型的分布式架构中用于并行处理业务的模式,具备较好的可伸缩性和稳定性。主进程不负责具体的业务处理,而是负责调度或管理工作进程,他是趋向于稳定为。工作进程负责具体的业务处理。
通过 fork() 复制的进程都是一个独立的进程,这个进程中有着独立的 V8 实例。 fork() 进程是昂贵的。Node 通过事件驱动的方式在单线程上解决了大并发的问题,启动多个进程只是为了充分将 CPU 资源利用起来,而不是为了解决并发问题
cluster 模块的属性和方法
isMaster属性,返回该进程是不是主进程isWorker属性,返回该进程是不是工作进程fork()方法,只能通过主进程调用,衍生出一个新的 worker 进程,返回一个 worker 对象disconnect([callback])方法,当所有的工作进程都断开链接后,运行主进程优雅的关闭setupMaster([settings])方法,用于修改 fork() 默认行为,一旦调用,将会按照cluster.settings进行设置。settings属性,用于配置,参数 exec: worker文件路径;args: 传递给 worker 的参数;execArgv: 传递给 Node.js 可执行文件的参数列表workers属性,表示当前活跃进程的 hash 表 key 为 worker 进程 id
cluster 模块的事件
fork事件,当新的工作进程被 fork 时触发,可以用来记录工作进程活动listening事件,当一个工作进程调用listen()后触发,事件处理器两个参数 worker:工作进程对象;address: 包括 address、port、addressTypeonline事件,复制好一个工作进程后,工作进程主动发送一条 online 消息给主进程,主进程收到消息后触发,回调参数 worker 对象disconnect事件,主进程和工作进程之间 IPC 通道断开后触发exit事件,有工作进程退出时触发,回调参数 worker 对象、code 退出码、signal 进程被 kill 时的信号setup事件,cluster.setupMaster()执行后触发message事件,当主进程接收到任意工作进程发送的消息后触发,参数 worker 对象、message 消息,handle 处理,旧版本没有 worker 参数
worker 对象的属性与方法
在一个主进程中使用
cluster.workers来获取 worker 对象;在一个工作进程中,使用cluster.worker来获取 worker 对象
id属性,返回当前 worker 的 进程编号process属性,返回 worker 所在的进程对象send()方法,发送一个消息给工作进程或者主进程,参数 message 、callbackdisconnect()方法,在一个工作进程中,调用此方法会关闭所有 server,并等待这些 server 的close事件执行,然后关闭 IPC 管道isConnected()方法,返回是否链接到主进程isDead()方法,返回工作进程是否被终止kill([signal])方法,kill 工作进程
// 主进程文件 index.js
const os = require('os');
const cluster = require('cluster');
// isMaster 是不是主进程
console.log(cluster.isMaster); // true
// 是不是 worker 工作进程
console.log(cluster.isWorker); // false
// setupMaster 修改 fork 默认行为
cluster.setupMaster({
exec: 'worker.js' // worker 进程之行文件的路径
})
if (cluster.isMaster) {
for (let i = 0; i < os.cpus().length; i++) {
// fork 衍生 worker 子进程
cluster.fork();
}
// workers 活跃的进程
console.log(cluster.workers);
cluster.on('fork', function (worker) {
console.log('fork = ' + worker.id)
})
cluster.on('listening', function (worker, address) {
console.log('worker ' + worker.id + ' listen ' + address.address + ':' + address.port);
})
cluster.on('online', function (worker) {
console.log('worker ' + worker.id + ' online');
})
cluster.on('disconnect', function (worker) {
console.log('disconnected worker ' + worker.id);
})
cluster.on('exit', function (worker, code, signal) {
console.log('exit ' + worker.id + ' code = ' + code + ' signal ' + signal);
})
cluster.on('setup', function () {
console.log('have setup');
})
cluster.on('message', function (worker, message, handle) {
console.log('got message from ' + worker.id + ' message ' + message);
worker.id === 2 && setTimeout(function () {
worker.send('disconnect');
}, 1000)
worker.id === 3 && setTimeout(function () {
worker.send('kill');
}, 1000)
})
}
worker 对象的事件
listening和cluster.on(‘listening’)事件类似,但针对特定的工作进程online和cluster.on(‘online’)事件类似,但针对特定的工作进程disconnect事件,主进程和工作进程之间 IPC 通道断开后触发exit事件,当前工作进程退出时触发,回调参数 code 退出码、signal 进程被 kill 时的信号message事件,当前进程接收主进程发送的消息后触发,message 消息,handle 处理,旧版本没有 worker 参数error事件,此事件和child_process.fork()提供的error事件相同
// 工作进程文件 worker.js
const cluster = require('cluster');
const http = require('http');
// cluster.worker 当前子进程
const worker = cluster.worker;
// id 进程编号
console.log('in worker id', worker.id);
// process worker 所在的进程对象
// console.log('in worker process', worker.process);
// isConnected 是否链接到主进程
console.log('worker ' + worker.id + ' connected ' + worker.isConnected());
// send 发送消息
worker.send('first message', function () {
console.log('first message callback');
})
worker.on('disconnect', function () {
console.log('worker ' + worker.id + ' disconnect in');
})
worker.on('error', function (err) {
console.log('worker ' + worker.id + ' error', err);
})
worker.on('exit', function (code, signal) {
console.log('worker ' + worker.id + ' exit ');
})
worker.on('listening', function (address) {
console.log('worker ' + worker.id + ' listerner', address);
})
worker.on('message', function (msg) {
if (msg === 'disconnect') {
// disconnect 断开链接
worker.disconnect();
//
console.log('worker is dead ' + worker.isDead());
}
if (msg === 'kill') {
// kill 结束进程
worker.kill();
}
})
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000, '10.15.32.49');更多推荐



所有评论(0)