NodeJS 跨语言子进程持续通讯
来源:互联网 发布:单片机uart是什么意思 编辑:程序博客网 时间:2024/05/01 20:41
有一个应用场景:用C/C++/Java等编译型语言做NodeJS服务器背后高性能计算的组件,那么应该如何实现?
比较好的方法是用Socket通讯,但这就需要双方都要进行套接字编程。
NodeJS倒是好说,但其他语言的套接字编程就不一定简单了。
所以本文中以NodeJS/C++为例,用管道来进行通讯,就像标准输入输出一样简单。
本文仅作演示,默认所有文件都在同一个目录。
高性能运算端
先以一个可以持续 输入/输出 循环的简单C++程序为例,使用阻塞输入可以很好地解决问题。
#include <iostream>using namespace std;int main() { int n; while(cin >> n) { cout << n + 1 << endl; } return 0;}
使用 scanf, printf, cin, cout 可以凭自己喜好,经测试都是可以的。
是否使用 endl, flush, \n 都是可以的。
然后编译成 a
(Windows:a.exe
;Unix:a.out
,不用在意这些细节)
NodeJS端
创建一个 i.js
的文件,并写入:
var path = require('path');var child = require('child_process').spawn(path.join(__dirname, 'a'));child.stdout.on('data', function (data) { console.log('stdout: ' + data);});child.stderr.on('data', function (data) { console.log('stderr: ' + data);});child.stdin.write('1\n'); // Attention!
注意child.stdin.write
的格式要与C++程序设计地一致。
本例中如果不加入\n
,C++中的输入不会结束,也就不会有输出,更不会触发child.stdout
的data
事件。
调用
child.stdin.end()
之后,C++程序会接收到 EOF 输入,正常的程序就会退出。所以在持续运行的程序中不要使用child.stdin.end()
方法。
使用
$ node i.js
即可看到
stdout: 2
跨模块组织
显然我们需要在多个NodeJS模块使用同一个C++进程的。
使用同一个进程的好处是,C++程序的生命周期变长,可持久化的数据结构得以持续存在。
child.js
var path = require('path');var spawn = require('child_process').spawn;var child = spawn(path.join(__dirname, 'a')); module.exports = child;
Middleware 模式
使用 child.stdout.on('data', (data) => { ... })
来设计 Middleware 模式,但数据不能通过中间件内部修改直接传递(因为参数 data 是 string 类型,不使用引用赋值),要另外封装一个变量来使用中间件。
Flash 模式
对于某个特定的输入,我们通常要用一个逻辑处理C++返回的输出。 caller1.js
var child = require('./child');function caller1(input) { child.stdout.once('data', function (data) { console.log('caller1 result: ' + data); }); child.stdin.write(input);}module.exports = caller1;
可以类似地构造一个 caller2 ,分别调用 caller1 与 caller2 的时候,会只使用对应的回调函数处理。
有个坑点是:如果在过短的时间内调用 caller1 与 caller2 时,caller1, caller2接收到数据是两者的和。
因为他们的监听器在数据回收之前就被加入data
事件的监听器列表里了。
解决方案是延迟后一个caller函数的执行时间,当前一个caller的once监听器执行之后再回调后面这个caller。
或者简单一些直接估计data
的反应时间(这不保险)
消息队列
Message Queue
为了解决之前提到的问题,我们不直接将消息写入child.stdin
,也不直接将监听器加到child.stdout
中。
sender.js
var child = require('./child');var messages = []; // MQ Container// fired when child responsechild.stdout.on('data', function (data) { // get handler and dequeue var action = messages.shift(); // call handler if exists action && action.handler && action.handler(data); // send next message to child if exists if (messages.length > 0) { child.stdin.write(messages[0].msg); }})// enqueue messagefunction send(msg, callbackFn) { // if no message in queue, send the first message to child if (messages.length === 0) { child.stdin.write(msg); } // enqueue the message and handler messages.push({ msg: msg, handler: callbackFn });}module.exports = send;
检查逻辑,你必须使得每次send都能得到C++程序的响应,不然就一直不会触发事件,导致MQ停滞,但NodeJS不会阻塞。
试用一下: main.js
var sender = require('./sender');sender('1\n', (data) => console.log('func 1: ' + data));sender('3\n', (data) => console.log('func 2: ' + data));console.log('MQ Enqueue Completed');
运行结果:
MQ Enqueue Completedfunc 1: 2func 2: 4
很好,基本没有问题,关于工程化的封装以后再说。
参考资料
Child Process | node.js API docs v6.x
- NodeJS 跨语言子进程持续通讯
- nodejs-进程与子进程
- NodeJs——子进程
- nodejs之child_process子进程模块
- Nodejs实现父进程与子进程资源共享
- Day29、加载子进程、Vfork创建子进程、进程间通讯(管道)
- 利用NodeJS的子进程(child_process)调用系统命令
- nodejs学习--子进程 child_process模块的基本介绍
- 利用NodeJS的子进程(child_process)调用系统命令
- 跨平台进程间通讯
- nodejs进程
- “进程通讯”
- 进程通讯
- 进程通讯
- 进程通讯
- 进程通讯
- flask笔记:12:flask与nodejs通讯跨域问题解决
- 父子进程PIPE通讯控制子进程输入执行自动化命令
- do{} while(0)
- Socket 基础解析使用ServerSocket建立聊天服务器
- 严重:The web application [web01] appears to have started a thread named ...
- 【代班大咖—这个夏天,和大咖一起愉快的聊天】李善平—程序员职业发展之路
- 导读ICML2016 - Learning Convolutional Neural Networks for Graphs
- NodeJS 跨语言子进程持续通讯
- 羽毛球单打和双打的有效边界区域
- 地图与定位(一)定位服务
- 关于Android studio导入百度地图API的方法
- java.lang.IllegalStateException: No activity
- 朝鮮歷史筆寫本原稿 (韓長庚編)
- WINDOWS中NEXUS的安装使用【ATCO整理】
- Android水波纹特效的简单实现
- CentOS 6.5 安装与配置LAMP FTP