深入理解Node.js中的进程管理


Posted in Javascript onMarch 13, 2017

前言

本文主要对 Node.js 中进程管理相关的东西做一个简单介绍,包括 process 对象、child_process 模块和cluster 模块,详细的 API 可以查看官方文档,下面来看看详细的介绍吧。

Process 对象

process 是 Node.js 的一个全局对象,可以在任何地方直接使用而不需要 require 命令加载。process 对象提供了 当前 node 进程 的命令行参数、标准输入输出、运行环境和运行状态等信息。

常用属性

argv

process.argv 属性返回一个数组,第一个元素是 node,第二个元素是脚本文件名称,其余成员是脚本文件的参数。

$ node process-2.js one two=three four

0: /usr/local/bin/node
1: /Users/mjr/work/node/process-2.js
2: one
3: two=three
4: four

env

process.env 返回一个对象,包含了当前 Shell 的所有环境变量,比如:

{
 TERM: 'xterm-256color',
 SHELL: '/bin/zsh',
 USER: 'huangtengfei',
 PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
 PWD: '/Users/huangtengfei',
 HOME: '/Users/huangtengfei'
}

这个属性通常的使用场景是,新建一个 NODE_ENV 变量,用来确定当前所处的开发阶段,生成阶段设为 production,开发阶段设为 develop ,然后在脚本中读取 process.env.NODE_ENV 再做相应处理即可。

运行脚本时可以这样改变环境变量:

$ export NODE_ENV=production && node app.js
# 或者
$ NODE_ENV=production node app.js

stdin/stdout

process.stdin 指向标准输入(键盘到缓冲区里的东西),返回一个可读的流:

process.stdin.setEncoding('utf8');

process.stdin.on('readable', () => {
 var chunk = process.stdin.read();
 if (chunk !== null) {
 process.stdout.write(`data: ${chunk}`);
 }
});

process.stdin.on('end', () => {
 process.stdout.write('end');
});

process.stdout 指向标准输出(向用户显示内容),返回一个可写的流:

const fs = require('fs');

fs.createReadStream('wow.txt')
 .pipe(process.stdout);

常用方法

cwd()

process.cwd() 返回运行 Node 的工作目录(绝对路径),比如在目录 /Users/huangtengfei/abc 下执行 node server.js,那么 process.cwd() 返回的就是 /Users/huangtengfei/abc。

另一个常用的获取路径的方法是 __dirname,它返回的是执行文件时该文件在文件系统中所在的目录。注意 process.cwd() __dirname 的不同,前者是进程发起时的位置,后者是脚本的位置,两者可能不一致。

on()

process 对象部署了 EventEmitter 接口,可以使用 process.on() 方法监听各种事件,并指定回调函数。比如监听到系统发出进程终止信号时关闭服务器然后退出进程:

process.on('SIGTERM', function () {
 server.close(function () {
 process.exit(0);
 });
});

exit()

process.exit() 会让 Node 立即终止当前进程(同步),参数为一个退出状态码,0 表示成功,大于 0 的任意整数表示失败。

kill()

process.kill() 用来对特定 id 的进程(process.pid)发送信号,默认为 SIGINT 信号。比如杀死当前进程:

process.kill(process.pid, 'SIGTERM');

虽然名字叫 kill ,但其实 process.kill() 只是负责发送信号,具体发送完信号之后这个怎么处理这个指定进程,取决于信号种类和接收到这个信号之后做了什么操作(比如 process.exit() 或者只是 console.log('Ignored this single'))。

Child Process 模块

child_process 模块用于创建和控制子进程,其中最核心的是 .spawn() ,其他 API 算是针对特定场景对它的封装。使用前要先 require 进来:

const cp = require('child_process');

exec(command[, options][, callback])

exec() 方法用于执行 shell 命令,它的第一个参数是字符串形式的命令,第二个参数(可选)用来指定子进程运行时的定制化操作,第三个参数(可选)用来设置执行完命令的回调函数。比如在一个特定目录 /Users/huangtengfei/abc 下执行 ls -l 命令:

cp.exec('ls -l', {
 cwd: '/Users/huangtengfei/abc'
}, (error, stdout, stderr) => {
 if (error) {
 console.error(`exec error: ${error}`);
 return;
 }
 console.log(`stdout: ${stdout}`);
 console.log(`stderr: ${stderr}`);
})

spawn(command[, args][, options])

spawn() 用来创建一个子进程执行特定命令,与 exec() 的区别是它没有回调函数,只能通过监听事件来获取运行结果,它适用于子进程长时间运行的情况,可以实时输出结果。

const ls = cp.spawn('ls', ['-l']);

ls.stdout.on('data', (data) => {
 console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
 console.log(`stderr: ${data}`);
});

ls.on('close', (code) => {
 console.log(`child process exited with code $[code]`);
});

使用 spawn 可以实现一个简单的守护进程,在工作进程不正常退出时重启工作进程:

/* daemon.js */
function spawn(mainModule) {
 const worker = cp.spawn('node', [ mainModule ]);

 worker.on('exit', function (code) {
  if (code !== 0) {
   spawn(mainModule);
  }
 });
}

spawn('worker.js');

fork(modulePath[, args][, options])

fork() 用来创建一个子进程执行 node 脚本,fork('./child.js') 相当于 spawn('node', ['./child.js']) , 区别在于 fork 会在父子进程之间建立一个通信管道(fork() 的返回值),用于进程间通信。对该通信管道对象可以监听 message 事件,用来获取子进程返回的信息,也可以向子进程发送信息。

/* main.js */
const proc = cp.fork('./child.js');
proc.on('message', function(msg) {
 console.log(`parent got message: ${msg}`);
});
proc.send({ hello: 'world' });

/* child.js */
process.on('message', function(msg) {
 console.log(`child got message: ${msg}`);
});
process.send({ foo: 'bar' });

Cluster 模块

Node.js 默认单进程执行,但这样就无法利用多核计算机的资源,cluster 模块的出现就是为了解决这个问题的。在开发服务器程序时,可以通过 cluster 创建一个主进程和多个 worker 进程,让每个 worker 进程运行在一个核上,统一通过主进程监听端口和分发请求。

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
 console.log(`Master ${process.pid} is running`);

 // Fork workers.
 for (let i = 0; i < numCPUs; i++) {
 cluster.fork();
 }

 cluster.on('exit', (worker, code, signal) => {
 console.log(`worker ${worker.process.pid} died`);
 });
} else {
 // Workers can share any TCP connection
 // In this case it is an HTTP server
 http.createServer((req, res) => {
 res.writeHead(200);
 res.end('hello world\n');
 }).listen(8000);

 console.log(`Worker ${process.pid} started`);
}

常用属性和方法

isMaster/isWorker

cluster.isMaster 用来判断当前进程是否是主进程,cluster.isWorker 用来判断当前进程是否是工作进程,两者返回的都是布尔值。

workers

cluster.workers 是一个包含所有 worker 进程的对象,key 为 worker.id,value 为 worker 进程对象。

// 遍历所有 workers
function eachWorker(callback) {
 for (const id in cluster.workers) {
 callback(cluster.workers[id]);
 }
}
eachWorker((worker) => {
 worker.send('big announcement to all workers');
});

fork([env])

cluster.fork() 方法用来新建一个 worker 进程,默认上下文复制主进程,只有主进程可调用。

常用事件

listening

在工作进程调用 listen 方法后,会触发一个 listening 事件,这个事件可以被 cluster.on('listening') 监听。

比如每当一个 worker 进程连进来时,输出一条 log 信息:

cluster.on('listening', (worker, address) => {
 console.log(
 `A worker is now connected to ${address.address}:${address.port}`);
});

exit

在工作进程挂掉时,会触发一个 exit 事件,这个事件可以被 cluster.on('exit') 监听。

比如自动重启 worker:

cluster.on('exit', (worker, code, signal) => {
 console.log('worker %d died (%s). restarting...',
 worker.process.pid, signal || code);
 cluster.fork();
});

worker 对象

worker 对象是 cluster.fork() 的返回值,代表一个 worker 进程。

worker.id

worker.id 是当前 worker 的唯一标识,也是保存在 cluster.workers 中的 key 值。

worker.process

所有的 worker 进程都是通过 child_process.fork() 生成的,这个进程对象保存在 worker.process 中。

worker.send()

worker.send() 用在主进程给子进程发送消息,在子进程中,使用 process.on() 监听消息并使用 process.send() 发送消息。

if (cluster.isMaster) {
 const worker = cluster.fork();
 worker.send('hi there');
} else if (cluster.isWorker) {
 process.on('message', (msg) => {
 process.send(msg);
 });
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
Firefox和IE浏览器兼容JS脚本写法小结
Jul 07 Javascript
jQuery创建自己的插件(自定义插件)的方法
Jun 10 Javascript
js Array操作的最简短最容易理解方法
Dec 09 Javascript
js中利用tagname和id获取元素的方法
Jan 03 Javascript
Bootstrap的Refresh Icon也spin起来
Jul 13 Javascript
Vuejs第十三篇之组件——杂项
Sep 09 Javascript
深入浅析JavaScript中的RegExp对象
Sep 18 Javascript
Vue.js如何使用Socket.IO的示例代码
Sep 05 Javascript
jQuery实现视频展示效果
May 30 jQuery
js简单实现自动生成表格功能示例
Jun 02 Javascript
vue自定义指令和动态路由实现权限控制
Aug 28 Javascript
一文了解JavaScript用Element Traversal新属性遍历子元素
Nov 27 Javascript
jQuery简易时光轴实现方法示例
Mar 13 #Javascript
JavaScript评论点赞功能的实现方法
Mar 13 #Javascript
JS+html5 canvas实现的简单绘制折线图效果示例
Mar 13 #Javascript
JS实现含有中文字符串的友好截取功能分析
Mar 13 #Javascript
JS实现直接运行html代码的方法
Mar 13 #Javascript
js从输入框读取内容,比较两个数字的大小方法
Mar 13 #Javascript
jsonp跨域请求实现示例
Mar 13 #Javascript
You might like
PHP 生成的XML以FLASH获取为乱码终极解决
2009/08/07 PHP
PHPMailer邮件发送的实现代码
2013/05/04 PHP
smarty模板引擎之内建函数用法
2015/03/30 PHP
PHP 9 大缓存技术总结
2015/09/17 PHP
PHP封装的MSSql操作类完整实例
2016/05/26 PHP
PHP实现基本留言板功能原理与步骤详解
2020/03/26 PHP
在IE和VB中支持png图片透明效果的实现方法(vb源码打包)
2011/04/01 Javascript
NodeJS中Buffer模块详解
2015/01/07 NodeJs
text-align:justify实现文本两端对齐 兼容IE
2015/08/19 Javascript
Jquery基础教程之DOM操作
2015/08/19 Javascript
javascript实现九宫格相加数值相等
2020/05/28 Javascript
解决URL地址中的中文乱码问题的办法
2017/02/10 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
深入理解Angular中的依赖注入
2017/06/26 Javascript
微信小程序如何再次获取用户授权的方法
2019/05/10 Javascript
vue-cli2与vue-cli3在一台电脑共存的实现方法
2019/09/25 Javascript
[01:45]DOTA2众星出演!DSPL刀塔次级职业联赛宣传片
2014/11/21 DOTA
Python判断Abundant Number的方法
2015/06/15 Python
浅谈python中的数字类型与处理工具
2017/08/02 Python
python 通过麦克风录音 生成wav文件的方法
2019/01/09 Python
python实现从本地摄像头和网络摄像头截取图片功能
2019/07/11 Python
Django使用中间键实现csrf认证详解
2019/07/22 Python
python遍历文件目录、批量处理同类文件
2019/08/31 Python
澳大利亚吉他在线:Artist Guitars
2017/03/30 全球购物
俄罗斯第一家篮球店:StreetBall
2020/07/30 全球购物
机械专业毕业生推荐信范文
2013/11/25 职场文书
旅游个人求职信范文
2014/01/30 职场文书
五年级英语教学反思
2014/01/31 职场文书
化妆品活动策划方案
2014/05/23 职场文书
校庆标语集锦
2014/06/25 职场文书
捐书仪式主持词
2015/07/04 职场文书
Python 如何实现文件自动去重
2021/06/02 Python
Vue vee-validate插件的简单使用
2021/06/22 Vue.js
简单且有用的Python数据分析和机器学习代码
2021/07/02 Python
Windows Server 修改远程桌面端口的实现
2022/06/25 Servers
分享很少见很有用的SQL功能CORRESPONDING
2022/08/05 MySQL