nodeJS

来源:互联网 发布:淘宝店铺装修设置单价 编辑:程序博客网 时间:2024/06/11 15:47

Process 进程 、child_process 子进程 、Cluster 集群

虽然 Node.js 把许多东西从操作系统中抽象出来,但你依然在操作系统中运行,而且可能想要更直接的与他交互,Node 中可以使用系统中已经存在的进程,或者创建新的子进程来做各种工作,虽然 Node 本身是一个 ‘胖’ 线程,带有单独一个事件循环,但你可以任意的开启其他进程( 线程)在事件循环外工作 – 《Node 即学即用》

参考

  • 深入浅出 Node.js
  • Node.js 文档
  • JavaScript 标准参考教程 Node.js

1. Process 进程

说明

process 对象是 Node 的一个全局对象,提供当前 Node 进程的信息,他可以在脚本的任意位置使用,不必通过 require 命令加载。

属性 API

  1. process.argv 属性,返回一个数组,包含了启动 node 进程时的命令行参数
  2. process.env 返回包含用户环境信息的对象,可以在 脚本中对这个对象进行增删改查的操作
  3. process.pid 返回当前进程的进程号
  4. process.platform 返回当前的操作系统
  5. process.version 返回当前 node 版本
  6. process.execPath 返回启动 Node.js 进程的可执行文件所在的绝对路径
  7. process.execArgv 返回 在 Node 可执行文件与脚本文件之间的命令行参数
  8. process.stdin 标准输入,实现 Duplex 流(可读、可写)
  9. process.stdout 标准输出,实现 Duplex 流(可读、可写),write() 方法相当于 console.log()
  10. 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

  1. process.cwd() 返回 node.js 进程当前工作目录
  2. process.chdir() 变更 node.js 进程的工作目录
  3. process.nextTick(fn) 将任务放到当前事件循环的尾部,添加到 ‘next tick’ 队列,一旦当前事件轮询队列的任务全部完成,在 next tick 队列中的所有 callback 会被依次调用
  4. process.exit() 退出当前进程,很多时候是不需要的
  5. process.kill(pid[,signal]) 给指定进程发送信号,包括但不限于结束进程
  6. 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 事件

  1. beforeExit 事件,在 Node 清空了 EventLoop 之后,再没有任何待处理任务时触发,可以在这里再部署一些任务,使得 Node 进程不退出,显示的终止程序时(process.exit()),不会触发
  2. exit 事件,当前进程退出时触发,回调函数中只允许同步操作,因为执行完回调后,进程金辉退出
  3. uncaughtException 事件,当前进程抛出一个没有捕获的错误时触发,可以用它在进程结束前进行一些已分配资源的同步清理操作,尝试用它来恢复应用的正常运行的操作是不安全的
  4. warning 事件,任何 Node.js 发出的进程警告,都会触发此事件
  5. 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. 创建方法的比较

  1. spawn():启动一个子进程来执行命令
  2. exec():启动一个子进程来执行命令,并且通过回调函数通知子进程状况
  3. execFile():启动一个子进程来执行可执行文件
  4. fork():是 spawn() 的一个特例,创建子进程只需要指定要执行的 javascript 文件模块即可,子进程返回一个 child_process 对象,并附加父子进程间的通信渠道
  5. exec() 与 execFile() 创建时可以指定 timeout 属性设置超时时间,一旦创建的进程运行超过设定的时间将会被杀死

子进程对象的事件与属性

事件

  1. close 事件,当子进程的 ‘stdio’ 流被关闭时触发
  2. disconnect 事件,在父进程中 subprocess.disconnect() 或者在子进程中调用 process.disconnect() 都会触发,断开后将关闭 IPC 通道,不能再发送或者接受消息
  3. error 事件,当子进程无法被复制创建、无法被杀死、无法发送消息时会触发
  4. exit 事件, 子进程结束后会触发,回调参数 code:退出码;signal: 被终止时的信号
  5. message 事件,父、子进程间通信,接受信息用

属性

  1. connected 属性,表明是否仍可以从一个子进程发送和接收消息
  2. disconnect() 方法,关闭父进程与子进程之间的 IPC 通道,一旦没有其他的连接使其保持活跃,则允许子进程正常退出。
  3. kill([signal]) 方法, 向子进程发送一个信号,不写参数默认结束子进程
  4. killed 属性,表明该子进程已经成功地被 subprocess.kill() 终止
  5. pid属性,子进程的进程标识
  6. 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 模块的属性和方法

  1. isMaster 属性,返回该进程是不是主进程
  2. isWorker 属性,返回该进程是不是工作进程
  3. fork() 方法,只能通过主进程调用,衍生出一个新的 worker 进程,返回一个 worker 对象
  4. disconnect([callback]) 方法,当所有的工作进程都断开链接后,运行主进程优雅的关闭
  5. setupMaster([settings]) 方法,用于修改 fork() 默认行为,一旦调用,将会按照cluster.settings进行设置。
  6. settings 属性,用于配置,参数 exec: worker文件路径;args: 传递给 worker 的参数;execArgv: 传递给 Node.js 可执行文件的参数列表
  7. workers 属性,表示当前活跃进程的 hash 表 key 为 worker 进程 id

cluster 模块的事件

  1. fork 事件,当新的工作进程被 fork 时触发,可以用来记录工作进程活动
  2. listening 事件,当一个工作进程调用 listen() 后触发,事件处理器两个参数 worker:工作进程对象;address: 包括 address、port、addressType
  3. online 事件,复制好一个工作进程后,工作进程主动发送一条 online 消息给主进程,主进程收到消息后触发,回调参数 worker 对象
  4. disconnect 事件,主进程和工作进程之间 IPC 通道断开后触发
  5. exit 事件,有工作进程退出时触发,回调参数 worker 对象、code 退出码、signal 进程被 kill 时的信号
  6. setup 事件,cluster.setupMaster() 执行后触发
  7. message 事件,当主进程接收到任意工作进程发送的消息后触发,参数 worker 对象、message 消息,handle 处理,旧版本没有 worker 参数

worker 对象的属性与方法

在一个主进程中使用 cluster.workers 来获取 worker 对象;在一个工作进程中,使用cluster.worker 来获取 worker 对象

  1. id 属性,返回当前 worker 的 进程编号
  2. process 属性,返回 worker 所在的进程对象
  3. send() 方法,发送一个消息给工作进程或者主进程,参数 message 、callback
  4. disconnect() 方法,在一个工作进程中,调用此方法会关闭所有 server,并等待这些 server 的 close 事件执行,然后关闭 IPC 管道
  5. isConnected() 方法,返回是否链接到主进程
  6. isDead() 方法,返回工作进程是否被终止
  7. 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 对象的事件

  1. listening 和cluster.on(‘listening’)事件类似,但针对特定的工作进程
  2. online 和cluster.on(‘online’)事件类似,但针对特定的工作进程
  3. disconnect 事件,主进程和工作进程之间 IPC 通道断开后触发
  4. exit 事件,当前工作进程退出时触发,回调参数 code 退出码、signal 进程被 kill 时的信号
  5. message 事件,当前进程接收主进程发送的消息后触发,message 消息,handle 处理,旧版本没有 worker 参数
  6. 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');
原创粉丝点击