当前位置: 首页 > news >正文

Nodejs进程间通信

普通模式

在 Node.js 里,通过 Child Process 模块 child_process.fork() 方法 fork 出来的子进程,提供了一个 chind.send 来进行进程间的消息通讯:

const cp = require('child_process');
const n = cp.fork(`${__dirname}/sub.js`);
 
// 监听  message  事件来接收子进程发送的消息
n.on('message', (m) => {
  console.log('PARENT got message:', m);
});
 
// 向子进程发送消息
n.send({ hello: 'world' });
// 监听 *message* 事件,接收来自父进程发送的消息
process.on('message', (m) => {
  console.log('CHILD got message:', m);
});

// 通过 process.send 向父进程发送消息
process.send({ foo: ‘bar’ });
实际上,通过 child_process.fork() 的方式 spawn 出来的子进程,默认激活了一个 IPC (进程间通信,Inter-Process Communication) 通道来进行通讯的。

但是使用 fork 方法有一个很大的局限性,既只能用于产生 Node.js 子进程,如果调用的是其它语言(比如其它诸多世界上最好的各种语言)的时候,就跪了,同时在很多场景下,单一的 IPC 通道并不能满足需求(性能、流式处理等),这个时候,就只能祭出 child_proecess.spawn() 来处理了。

进阶处理
Node.js 一共提供了四种方式来产生子进程:

  • child_process.exec
  • child_process.execFile
  • child_process.fork
  • child_process.spawn

实际上,前三种方法都是对 child_process.spawn 的特殊封装用于简化调用,那么既然 child_process.fork 能有方式激活 IPC 通道来进行进程间的通讯,直接 child_process.spawn 产生的子进程激活通讯通道也就不是什么问题了。

调用 child_process.spawn 时,有三个参数需要处理:

  • command: 用来调用的命令
  • args:用来传递给 command 参数调用的命令的参数
  • options:一组产生子进程时的配置参数
    我们需要的东西,就在 options.stdio 里。

options.stdio 用于配置建立父进程与子进程之间的通讯管道,他的值可以是一个数组或者字符串,当为数组时,每个索引对应子进程中的一个文件标识符,默认情况下,Node.js 会为其 spawn 的子进程打开三个文件标识符,既子进程的 stdin 、stdoutstderr 流会被重定向到 * ChildProcess* 对象的 child.stdin, child.stdout 和 child.stderr 上,而当 options.stdio 的值为字符串时,对应下面的三种情况:

  • ‘pipe’:等价于 [‘pipe’, ‘pipe’, ‘pipe’] (默认情况)
  • ‘ignore’:等价于 [‘ignore’, ‘ignore’, ‘ignore’]
  • ‘inherit’:等价于 [process.stdin, process.stdout, process.stderr] 或者 [0,1,2]

在实际使用中,我们可以根据需要调整 options.stdio 的配置,比如忽略 stdin 和 stdout,并把 child 的 stderr 重定向到 process.stderr:

// parent.js
'use strict';
 
const child_process = require('child_process');
let worker = child_process.spawn('node', ['child.js'], {
    stdio: ['ignore', 'ignore', process.stderr]
});
 
// child.js
'use strict';
 
console.log('Hello world!');
console.error('Hello error!');  

此时执行 parent.js,仅会输出 Hello error!,因为我们配置忽略了 stdout 的输出。通过这三个值的灵活配置,我们可以实现多种样式的数据输入、输出。

高阶模式

默认情况下, child_process 在 spawn 的时候,只打开了三个文件标识符,而实际上,这个数量是可以继续扩展的,同时用于通讯的除了上面提到的 pipe 和 IPC,还可以直接使用 Stream,所以,你是不是已经想到了一些美妙的事情了:)

下面我们来扩展一下上面的例子,添加一条额外的 Stream 用于从 child 向 parent 发送数据。

// parent.js
'use strict';
 
const child_process = require('child_process');
 
let worker = child_process.spawn('node', ['child.js'], {
    stdio: ['ignore', 'ignore', process.stderr, 'pipe']
});
 
// 监听创建的 Stream
worker.stdio[3].on('data', (chunk) => {
    console.log(chunk.toString());
});
 
 
// child.js
'use strict';
 
const fs = require('fs');
 
let ws = fs.createWriteStream(null, {
    fd: 3
});
 
let doit = () => {
    ws.write(`Hello world! ${Date.now()}`, () => {
        setTimeout(doit, 1000);
    });
};
 
doit();

上面的实例中,因为我们明确知道当前打开的第四个文件标识符(索引从0开始)是我们在父进程中通过 ‘pipe’ 参数指定的,因此我们可以使用 fs.createWriteStream 的高阶用法,通过指定 fd 来创建一个 WriteStream。

使用同样的方式,可以继续扩充添加 stream 用于数据交换,从而实现进程间高效的数据交互。

实际上,这种进程间通讯的方式在各种语言中都是通用的,比如在之前阿里云的消息队列没有提供 Node SDK的情况下,因为业务需要,跟当时的小伙伴一起,利用这种方式,spawn 了一个PHP的子进程来调用阿里云的消息队列并进行数据交互,比较低成本的解决了一个很急业务需求。

相关文章:

  • C++中的STL——stack类的基本使用
  • 四信AI睿析—边缘智脑:赋能农业新时代,开启智慧种植新篇章
  • 【Web】DASCTF X CBCTF 2022九月挑战赛 题解
  • 注意力机制中多层的作用
  • JAVAEE——IP协议
  • 【C语言】内存函数-memcpy-memmove-memset...用法及实现,沉淀自己!
  • InnoDB高级特性篇(5)-使用InnoDB的全文索引
  • C#中全局处理异常方式
  • 数据结构和算法笔记6:KMP算法
  • Flink Catalog
  • 速盾:cdn服务器怎么做
  • 产品渲染3D效果图一张多少钱,哪个平台更有性价比?
  • VMwareWorkStation如何添加万兆网卡,万兆网卡添加教程
  • Android-Jetpack Compose的简单运用
  • 振弦采集模块的信号检测与分析计算
  • 后端存储实战课——高速增长篇
  • [附源码]计算机毕业设计基于SpringBoot的高校课程知识库
  • 项目管理逻辑:为什么职能部门官僚主义气息浓重?
  • [附源码]计算机毕业设计现代诗歌交流平台Springboot程序
  • Android使用ListView,DrawerLayout实现简单注册功能界面
  • Java基础:Object类、常用API
  • [附源码]Python计算机毕业设计Django少儿节目智能推荐系统
  • java认证与证书
  • 【LIN总线测试】——LIN主节点网络管理测试
  • 【Autopsy数字取证篇】Autopsy案例分析报告导出
  • 5 个用于复古图像着色的开源 Python 工具
  • sql serve数据库基础入门(2)
  • ProxyChains图文教程
  • 【torch.nn.Parameter 】参数相关的介绍和使用
  • vue是怎么初始化数据并挂载的?
  • MYSQL用函数请三思
  • Spring_第3章_AOP+事务