kekobin/blog

Node基础篇之进程process

Opened this issue · 0 comments

简介

通过process能够获得node进程相关的信息,比如node运行时的命令行参数,当前环境变量等,在编写cli脚手架时特别有用。

process.env

一般在部署node项目时,不同的环境需要配置不同的环境变量,常用的是:

NODE_ENV=development

然后在业务逻辑中可以使用process.env获取到环境变量,进行不同的处理:

if(process.env.NODE_ENV === 'development') ...

process.nextTick(fn)

process.nextTick(fn) 跟 setTimeout(fn, 0) 很像,但实际有实现及性能上的差异:

  • process.nextTick(fn) 将 fn 放到 node 事件循环的 下一个tick 里;
  • process.nextTick(fn) 比 setTimetout(fn, 0) 性能高;

process.argv

process.argv 返回一个数组,数组元素分别如下:

  • 元素1:node运行路径
  • 元素2:可执行文件的绝对路径
  • 元素x:其他,比如参数等

在 G:\node-l目录先编写argv.js

process.argv.forEach((val, index, array) => {
  console.log('参数' + index + ': ' + val);
});

终端执行 node argv.js aaa bbb,结果如下:
image

process.cwd() vs process.chdir(directory)

  • process.cwd():返回当前工作路径
  • process.chdir(directory):切换当前工作路径

对于process.chdir(),一般在编写cli脚手架时,很多时候需要动态切换到别的目录作为当前的node进程目录,然后执行比如 npm install等。

IPC相关

  • process.connected:如果当前进程是子进程,且与父进程之间通过IPC通道连接着,则为true;
  • process.disconnect():断开与父进程之间的IPC通道,此时会将 process.connected 置为false;

首先是 connected.js,通过 fork 创建子进程(父子进程之间创建了IPC通道)

var child_process = require('child_process');

child_process.fork('./connectedChild.js', {
  stdio: 'inherit'
});

然后,在 connectedChild.js 里面。

console.log( 'process.connected: ' + process.connected );
process.disconnect();
console.log( 'process.connected: ' + process.connected );

// 输出:
// process.connected: true
// process.connected: false

process.stdin、process.stdout、process.stderr

分别代表进程的标准输入、标准输出、标准错误输出.

image

例子一: 输出用户输入的数据

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');
});

在终端启动 node std.js, 然后输入 hello, 将打印 data: hello。

例子二: 输出用户输入的a+b结果

// 默认情况下输入流是关闭的,要监听输入流数据,首先要开启输入流
process.stdin.resume();
var a;
var b;
process.stdout.write("请输入a的值:");
process.stdin.on('data',function(chunk){
    //判断用户输入的是a的值还是b的值
    if(!a){
        a = Number(chunk);
        process.stdout.write("请输入b的值:");
    }else{
        b = Number(chunk);
        process.stdout.write("结果等于"+(a+b));
    }
})

进程的输入输出方法

process.on('exit', function(code) { console.log(code) });
process.stdin.setEncoding('utf8');
 
process.stdout.write("......\n");
process.stdout.write("确认执行吗(y/n)?");
process.stdin.on('data',(input)=>{
  input = input.toString().trim();
  if (['Y', 'y', 'YES', 'yes'].indexOf(input) > -1) { console.log('console log -- yes')};
  if (['N', 'n', 'NO', 'no'].indexOf(input) > -1) process.exit(0);
})

node可执行程序相关信息

  • process.version:返回当前node的版本,比如'v6.1.0'。

  • process.versions:返回node的版本,以及依赖库的版本,如下所示。
    image

  • pocess.execPath: node可执行程序的绝对路径,比如 '/usr/local/bin/node'
    image

进程运行所在环境

  • process.arch:返回当前系统的处理器架构(字符串),比如'arm', 'ia32', or 'x64'。
  • process.platform:返回关于平台描述的字符串,比如 darwin、win32 等。

终止进程:process.exit([exitCode])、process.exitCode

  • process.exit([exitCode]) 可以用来立即退出进程。即使当前有操作没执行完,比如 process.exit() 的代码逻辑,或者未完成的异步逻辑。
  • 写数据到 process.stdout 之后,立即调用 process.exit() 是不保险的,因为在node里面,往 stdout 写数据是非阻塞的,可以跨越多个事件循环。于是,可能写到一半就跪了。比较保险的做法是,通过process.exitCode设置退出码,然后等进程自动退出。
  • 如果程序出现异常,必须退出不可,那么,可以抛出一个未被捕获的error,来终止进程,这个比 process.exit() 安全。

master-worker 多进程端口不会占用原理

node的cluster模块在fork代码时,不会报端口被占用,这是什么原因呢?
答案是:master进程监听了端口,而内部根据当前进程是否是worker把worker的listen给hack掉了,因为请求会先到达master,此时master把改请求根据负载均衡传递给某个worker,达到最终的效果。

模拟代码:

//master.js
onst fork = require('child_process').fork;
const cpus = require('os').cpus();
const server = require('net').createServer();
server.listen(3000);
process.title = 'node-master'

for (let i=0; i<cpus.length; i++) {
    const worker = fork('worker.js');
    // 重点是把master的server句柄传给了子进程
    worker.send('server', server);
}
// worker.js
const http = require('http');
const server = http.createServer((req, res) => {
    res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid);
});// 注意:这里没有监听任何端口

let worker;
process.title = 'node-worker'
process.on('message', function (message, sendHandle) {
    if (message === 'server') {
        worker = sendHandle;
        // 使用master传过来的server,监听其请求,然后代理到子进程的server,就将该请求传给了该子进程
        worker.on('connection', function(socket) {
            server.emit('connection', socket);
        });
    }
});

net.Server都有connection事件,每次请求过来时,都会响应connection。

参考

Node.js process 模块解读