在nodejs中创建child process的方法

简介

nodejs的main event loop是单线程的,nodejs本身也维护着Worker Pool用来处理一些耗时的操作,我们还可以通过使用nodejs提供的worker_threads来手动创建新的线程来执行自己的任务。

本文将会介绍一种新的执行nodejs任务的方式,child process。

child process

lib/child_process.js提供了child_process模块,通过child_process我们可以创建子进程。

注意,worker_threads创建的是子线程,而child_process创建的是子进程。

在child_process模块中,可以同步创建进程也可以异步创建进程。同步创建方式只是在异步创建的方法后面加上Sync。

创建出来的进程用ChildProcess类来表示。

我们看下ChildProcess的定义:

interface ChildProcess extends events.EventEmitter {
 stdin: Writable | null;
 stdout: Readable | null;
 stderr: Readable | null;
 readonly channel?: Pipe | null;
 readonly stdio: [
  Writable | null, // stdin
  Readable | null, // stdout
  Readable | null, // stderr
  Readable | Writable | null | undefined, // extra
  Readable | Writable | null | undefined // extra
 ];
 readonly killed: boolean;
 readonly pid: number;
 readonly connected: boolean;
 readonly exitCode: number | null;
 readonly signalCode: NodeJS.Signals | null;
 readonly spawnargs: string[];
 readonly spawnfile: string;
 kill(signal?: NodeJS.Signals | number): boolean;
 send(message: Serializable, callback?: (error: Error | null) => void): boolean;
 send(message: Serializable, sendHandle?: SendHandle, callback?: (error: Error | null) => void): boolean;
 send(message: Serializable, sendHandle?: SendHandle, options?: MessageOptions, callback?: (error: Error | null) => void): boolean;
 disconnect(): void;
 unref(): void;
 ref(): void;

 /**
  * events.EventEmitter
  * 1. close
  * 2. disconnect
  * 3. error
  * 4. exit
  * 5. message
  */
 ...
 }

可以看到ChildProcess也是一个EventEmitter,所以它可以发送和接受event。

ChildProcess可以接收到event有5种,分别是close,disconnect,error,exit和message。

当调用父进程中的 subprocess.disconnect() 或子进程中的 process.disconnect() 后会触发 disconnect 事件。

当出现无法创建进程,无法kill进程和向子进程发送消息失败的时候都会触发error事件。

当子进程结束后时会触发exit事件。

当子进程的 stdio 流被关闭时会触发 close 事件。 注意,close事件和exit事件是不同的,因为多个进程可能共享同一个stdio,所以发送exit事件并不一定会触发close事件。

看一个close和exit的例子:

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);

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

ls.on('close', (code) => {
 console.log(`子进程使用代码 $[code] 关闭所有 stdio`);
});

ls.on('exit', (code) => {
 console.log(`子进程使用代码 $[code] 退出`);
});

最后是message事件,当子进程使用process.send() 发送消息的时候就会被触发。

ChildProcess中有几个标准流属性,分别是stderr,stdout,stdin和stdio。

stderr,stdout,stdin很好理解,分别是标准错误,标准输出和标准输入。

我们看一个stdout的使用:

const { spawn } = require('child_process');

const subprocess = spawn('ls');

subprocess.stdout.on('data', (data) => {
 console.log(`接收到数据块 ${data}`);
});

stdio实际上是stderr,stdout,stdin的集合:

readonly stdio: [
  Writable | null, // stdin
  Readable | null, // stdout
  Readable | null, // stderr
  Readable | Writable | null | undefined, // extra
  Readable | Writable | null | undefined // extra
 ];

其中stdio[0]表示的是stdin,stdio[1]表示的是stdout,stdio[2]表示的是stderr。

如果在通过stdio创建子进程的时候,这三个标准流被设置为除pipe之外的其他值,那么stdin,stdout和stderr将为null。

我们看一个使用stdio的例子:

const assert = require('assert');
const fs = require('fs');
const child_process = require('child_process');

const subprocess = child_process.spawn('ls', {
 stdio: [
 0, // 使用父进程的 stdin 用于子进程。
 'pipe', // 把子进程的 stdout 通过管道传到父进程 。
 fs.openSync('err.out', 'w') // 把子进程的 stderr 定向到一个文件。
 ]
});

assert.strictEqual(subprocess.stdio[0], null);
assert.strictEqual(subprocess.stdio[0], subprocess.stdin);

assert(subprocess.stdout);
assert.strictEqual(subprocess.stdio[1], subprocess.stdout);

assert.strictEqual(subprocess.stdio[2], null);
assert.strictEqual(subprocess.stdio[2], subprocess.stderr);

通常情况下父进程中维护了一个对子进程的引用计数,只有在当子进程退出之后父进程才会退出。

这个引用就是ref,如果调用了unref方法,则允许父进程独立于子进程退出。

const { spawn } = require('child_process');

const subprocess = spawn(process.argv[0], ['child_program.js'], {
 detached: true,
 stdio: 'ignore'
});

subprocess.unref();

最后,我们看一下如何通过ChildProcess来发送消息:

subprocess.send(message[, sendHandle[, options]][, callback])

其中message就是要发送的消息,callback是发送消息之后的回调。

sendHandle比较特殊,它可以是一个TCP服务器或socket对象,通过将这些handle传递给子进程。子进程将会在message事件中,将该handle传递给Callback函数,从而可以在子进程中进行处理。

我们看一个传递TCP server的例子,首先看主进程:

const subprocess = require('child_process').fork('subprocess.js');

// 打开 server 对象,并发送该句柄。
const server = require('net').createServer();
server.on('connection', (socket) => {
 socket.end('由父进程处理');
});
server.listen(1337, () => {
 subprocess.send('server', server);
});

再看子进程:

process.on('message', (m, server) => {
 if (m === 'server') {
 server.on('connection', (socket) => {
 socket.end('由子进程处理');
 });
 }
});

可以看到子进程接收到了server handle,并且在子进程中监听connection事件。

下面我们看一个传递socket对象的例子:

onst { fork } = require('child_process');
const normal = fork('subprocess.js', ['normal']);
const special = fork('subprocess.js', ['special']);

// 开启 server,并发送 socket 给子进程。
// 使用 `pauseOnConnect` 防止 socket 在被发送到子进程之前被读取。
const server = require('net').createServer({ pauseOnConnect: true });
server.on('connection', (socket) => {

 // 特殊优先级。
 if (socket.remoteAddress === '74.125.127.100') {
 special.send('socket', socket);
 return;
 }
 // 普通优先级。
 normal.send('socket', socket);
});
server.listen(1337);

subprocess.js的内容:

process.on('message', (m, socket) => {
 if (m === 'socket') {
 if (socket) {
 // 检查客户端 socket 是否存在。
 // socket 在被发送与被子进程接收这段时间内可被关闭。
 socket.end(`请求使用 ${process.argv[2]} 优先级处理`);
 }
 }
});

主进程创建了两个subprocess,一个处理特殊的优先级, 一个处理普通的优先级。

异步创建进程

child_process模块有4种方式可以异步创建进程,分别是child_process.spawn()、child_process.fork()、child_process.exec() 和 child_process.execFile()。

先看一个各个方法的定义:

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

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

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

child_process.execFile(file[, args][, options][, callback])

其中child_process.spawn是基础,他会异步的生成一个新的进程,其他的fork,exec和execFile都是基于spawn来生成的。

fork会生成新的Node.js 进程。

exec和execFile是以新的进程执行新的命令,并且带有callback。他们的区别就在于在windows的环境中,如果要执行.bat或者.cmd文件,没有shell终端是执行不了的。这个时候就只能以exec来启动。execFile是无法执行的。

或者也可以使用spawn。

我们看一个在windows中使用spawn和exec的例子:

// 仅在 Windows 上。
const { spawn } = require('child_process');
const bat = spawn('cmd.exe', ['/c', 'my.bat']);

bat.stdout.on('data', (data) => {
 console.log(data.toString());
});

bat.stderr.on('data', (data) => {
 console.error(data.toString());
});

bat.on('exit', (code) => {
 console.log(`子进程退出,退出码 $[code]`);
});
const { exec, spawn } = require('child_process');
exec('my.bat', (err, stdout, stderr) => {
 if (err) {
 console.error(err);
 return;
 }
 console.log(stdout);
});

// 文件名中包含空格的脚本:
const bat = spawn('"my script.cmd"', ['a', 'b'], { shell: true });
// 或:
exec('"my script.cmd" a b', (err, stdout, stderr) => {
 // ...
});

同步创建进程

同步创建进程可以使用child_process.spawnSync()、child_process.execSync() 和 child_process.execFileSync() ,同步的方法会阻塞 Node.js 事件循环、暂停任何其他代码的执行,直到子进程退出。

通常对于一些脚本任务来说,使用同步创建进程会比较常用。

到此这篇关于在nodejs中创建child process的方法的文章就介绍到这了,更多相关nodejs中创建child process内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解nodejs中的process进程

    虽然node对操作系统做了很多抽象的工作,但是你还是可以直接和他交互,比如和系统中已经存在的进程进行交互,创建工作子进程.node是一个用于事件循环的线程,但是你可以在这个事件循环之外创建其他的进程(线程)参与工作. 进程模块 process模块允许你获得或者修改当前node进程的设置,不想其他的模块,process是一个全局进程(node主进程),你可以直接通过process变量直接访问它. process实现了EventEmitter接口,exit方法会在当进程退出的时候执行.因为进程退出之

  • 利用NodeJS的子进程(child_process)调用系统命令的方法分享

    NodeJS子进程简介 NodeJS子进程提供了与系统交互的重要接口,其主要API有: 标准输入.标准输出及标准错误输出的接口. NodeJS子进程简介 NodeJS 子进程提供了与系统交互的重要接口,其主要 API 有: 标准输入.标准输出及标准错误输出的接口 child.stdin 获取标准输入 child.stdout 获取标准输出 child.stderr 获取标准错误输出 获取子进程的PID:child.pid 提供生成子进程的重要方法:child_process.spawn(cmd,

  • 在nodejs中创建child process的方法

    简介 nodejs的main event loop是单线程的,nodejs本身也维护着Worker Pool用来处理一些耗时的操作,我们还可以通过使用nodejs提供的worker_threads来手动创建新的线程来执行自己的任务. 本文将会介绍一种新的执行nodejs任务的方式,child process. child process lib/child_process.js提供了child_process模块,通过child_process我们可以创建子进程. 注意,worker_threa

  • 浅谈nodejs中创建cluster

    目录 cluster集群 cluster详解 cluster中的event cluster中的方法 cluster中的属性 cluster中的worker 总结 cluster集群 我们知道,nodejs的event loop或者说事件响应处理器是单线程的,但是现在的CPU基本上都是多核的,为了充分利用现代CPU多核的特性,我们可以创建cluster,从而使多个子进程来共享同一个服务器端口. 也就是说,通过cluster,我们可以使用多个子进程来服务处理同一个端口的请求. 先看一个简单的http

  • C# 在PDF文档中创建表格的实现方法

    表格能够直观的传达数据信息,使信息显得条理化,便于阅读同时也利于管理.那在PDF类型的文档中如何来添加表格并且对表格进行格式化操作呢?使用常规方法直接在PDF中添加表格行不通,那我们可以在借助第三方组件的情况下来实现.本篇文章中将介绍如何正确使用组件Free Spire.PDF for .NET添加表格到PDF.该组件提供了两个类PdfTable和PdfGrid用于创建表格,在进行代码编辑前,需先安装,添加Spire.PDF. dll到项目程序集中,同时添加到命名空间.下面是两种方法来添加表格的

  • Java中创建ZIP文件的方法

    java创建zip文件的代码如下如下: import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; public cla

  • nodejs中使用多线程编程的方法实例

    在以前的博文别说不可能,nodejs中实现sleep中,我向大家介绍了nodejs addon的用法.今天的主题还是addon,继续挖掘c/c++的能力,弥补nodejs的弱点. 我曾多次提到过nodejs的性能问题.其实就语言本身而言,nodejs的性能还是很高的,虽然不及大多部静态语言,但差距也并不大:相对其他动态语言而言,速度优势非常明显.但为什么我们常常说nodejs不能胜任CPU密集型场景呢?因为由于其单线程特性,对于CPU密集型场景,它并不能充分利用CPU.计算机科学中有一个著名的A

  • Javascript 中创建自定义对象的方法汇总

    Javascript 中创建对象,可以有很多种方法. Object构造函数/对象字面量: 抛开设计模式不谈,使用最基本的方法,就是先调用Object构造函数创建一个对象,然后给对象添加属性. 复制代码 代码如下: var student = new Object();      student.name = "xiao ming";      student.age = 20;      student.getName = function () {          alert(th

  • 在Bash脚本中创建和使用数组方法总结

    在Bash中定义一个数组 有两种方法可以在bash脚本中创建新数组.第一个是使用declare命令来定义一个Array.此命令将定义名为test_array的关联数组. $ declare -a test_array 还可以通过分配元素来创建数组. $ test_array=(apple orange lemon) 访问数组元素 与其他编程语言类似,bash数组元素可以使用索引号从0开始,然后从1.2.3-n开始访问.这也适用于索引号为数字的关联数组. $ echo ${test_array[0

  • 详解在Python的Django框架中创建模板库的方法

    不管是写自定义标签还是过滤器,第一件要做的事是创建模板库(Django能够导入的基本结构). 创建一个模板库分两步走: 第一,决定模板库应该放在哪个Django应用下. 如果你通过 manage.py startapp 创建了一个应用,你可以把它放在那里,或者你可以为模板库单独创建一个应用. 我们更推荐使用后者,因为你的filter可能在后来的工程中有用. 无论你采用何种方式,请确保把你的应用添加到 INSTALLED_APPS 中. 我们稍后会解释这一点. 第二,在适当的Django应用包里创

  • SQL Server 2008 数据库中创建只读用户的方法

    在SQL Server 2008中,为了保护数据库的安全,需要给不同的使用者开通不同的访问用户,那么如何简单的控制用户的权限呢?下面我们就创建一个只读用户,给大家学习使用. SQL Server 2008 Microsoft SQLServer Management Studio 1.首先打开[Microsoft SQLServer Management Studio],用管理员账户登录.这里我选的服务器是[local],账户是[windows身份验证],如果是连接远程的服务器,输入远程服务器地

  • 手把手教你在.NET中创建Web服务实现方法

    最近发现在.NET平台下使用Web服务还是很简单的.下面举个在.NET平台下创建Web服务的简单例子.首先用Visul Studio .Net创建一个C# 项目Asp.Net Web服务程序,源代码如下: 复制代码 代码如下: using System;using System.Collections;using System.ComponentModel;using System.Data;using System.Diagnostics;using System.Web;using Syst

随机推荐