如何在nodejs中实现兄弟进程通信
时间:2022-07-26
本文章向大家介绍如何在nodejs中实现兄弟进程通信,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
背景
在nodejs主进程中,开启一个额外的子进程A,进程A负责和线程池通信,完成cpu密集型的任务。通过nodejs主进程创建出来的多个nodejs工作进程可以把任务提交到进程A,然后拿到处理结果。
问题
尽管我们可以在主进程中保存工作进程的实例,但是想通过主进程,完成工作进程和进程A的通信还是非常麻烦,步骤如下
1 首先主进程要监听每个工作进程发过来的任务
2 然后把任务传给进程A
3 进程A处理完后,通知主进程,主进程再把结果发给对任务对应的子进程 其中,工作进程需要保存任务对应的上下文(比如说回调),因为工作进程可能同时给主进程发送了多个任务,当主进程通知工作进程某个任务完成的时候,工作进程需要通过任务找到对应的上下文,然后进行下一步处理,比如执行回调。
解决方案
在主进程中开启一个服务,实现没有继承关系的子进程间通信,选取的进程间通信方式是unix域,没有选tcp是因为同主机的进程间通信,使用tcp过于重和低效(需要经过协议栈的封包和解包)。子进程可以通过该服务和主进程通信,然后主进程转发请求给处理cpu型任务的子进程。结构如下
在主进程而不是进程A中开启unix域服务是因为以后新增处理其他任务的子进程时,可以复用该unix域服务,起到api网关的作用。但是多了一层,会多了一些通信的成本。更直接的可以使用以下结构
具体实现
客户端
const net = require('net');
const { EventEmitter } = require('events');
class Work extends EventEmitter {}
class UnixDomainClient extends EventEmitter {
constructor(options) {
super();
this.options = options;
}
send(data) {
const work = new Work();
const socket = net.connect(this.options.path);
socket.end(JSON.stringify(data));
socket.on('error', (e) => {
work.emit('error', e);
});
let res = null;
socket.on('data', (chunk) => {
res = res ? Buffer.concat([res, chunk]) : chunk;
});
socket.on('end', () => {
work.emit('message', res && res.toString());
});
return work;
}
}
const work = new UnixDomainClient({path: '/tmp/test.sock'}).send('hello');
work.on('message', function(res) {
console.log(res);
})
服务器
const fs = require('fs');
const net = require('net');
const constants = {
UNIX_PATH: '/tmp/test.sock',
}
if (fs.existsSync(constants.UNIX_PATH)) {
fs.unlinkSync(constants.UNIX_PATH);
}
const server = net.createServer({ allowHalfOpen: true }, (client) => {
let data = null;
client.on('data', (chunk) => {
data = data ? Buffer.concat([data, chunk]) : chunk;
});
client.on('end', () => {
console.log(`recive msg: ${data.toString()}`)
client.end('world');
});
});
server.listen(constants.UNIX_PATH, () => {
console.log(`bind uinx path ${constants.UNIX_PATH}`);
});
server.on('error', (error) => {
console.log(`unix domain server error ${error.toString()}`);
});
process.on('exit', () => {
if (fs.existsSync(constants.UNIX_PATH)) {
fs.unlinkSync(constants.UNIX_PATH);
}
});
- 【Spring实战】—— 15 Spring JDBC模板使用
- 前端开发总览
- 【Spring实战】—— 16 基于JDBC持久化的事务管理
- 【Spring实战】—— 4 Spring中bean的init和destroy方法讲解
- 基于AngularJS的过滤与排序
- 【Spring实战】—— 5 设值注入
- 科学家预测:未来100万年人类将变成半机械人类
- 【Spring实战】—— 8 自动装配
- 【Spring实战】—— 7 复杂集合类型的注入
- 【Spring实战】—— 6 内部Bean
- 几款可替代Dreamweaver的HTML5开发工具
- Linux下的Telnet设置方法介绍
- 2017年11月互联网和相关服务业保持快速增长
- 深度学习胸部x射线
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PyCharm 在Windows的有用快捷键详解
- Python 自动化测试(三): pytest 参数化测试用例构建
- 基于Android平台实现拼图小游戏
- kotlin项目加入Glide图片加载库并使用GlideApp的方法
- Android实现百分比下载进度条效果
- 实验2 OpenGL交互
- 深入了解OkHttp3之Interceptors
- 实验3.1 直线光栅化(键盘交互版)
- 150行Python代码实现带界面的数独游戏
- 实验4 编码裁剪算法
- 浅谈Python中os模块及shutil模块的常规操作
- Python decorator拦截器代码实例解析
- 实验4.1 编码裁剪算法(鼠标交互版)
- python实现对变位词的判断方法
- python实现一个猜拳游戏