Node与Python 双向通信的实现代码


Posted in Javascript onJuly 16, 2021

第三方数据供应商把数据和Python封装到一起,只能通过调用 Python方法来实现数据查询,如果可以通过Node 简单封装下实现 Python 方法调用可以快速上线并节省开发成本。

最简单粗暴的通信方式是 Nodejs调用一下 Python 脚本,然后获取子进程的输出,但是由于每次 Python 启动并加载数据包的过程比较漫长,所以对该过程优化。

进程通信

index.py

# 封装的 Python 包, 体积巨大
from mb import MB
# 从数据包中查询
mbe.get('1.0.1.0')

index.js

const { spawn } = require('child_process');
const ls = spawn('python3', ['index.py']);

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

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

ls.on('close', (code) => {
  console.log(`child process exited with code $[code]`);
});

通过child_process.spawn来派生 Python 子进程,监听 stdout 输出。上述方式也是官方文档中的示例,目前该示例存在两个问题:

  • Nodejs 没有向 Python 发送数据
  • Nodejs 调用完毕后,Python 子进程会退出;下次查询需要再次调用Python命令进行加载文件,查询数据;无法实现一次内存加载,多次使用。

进程双向通信

保证一次数据加载,多次使用的前提是 Python 进程启动后不能退出。Python 进程之所以退出是因为无事可做,所以常见的手段有循环,sleep,监听端口,这些手段可以翻译成同步阻塞任务,同步非阻塞任务,其中代价最小的就是同步非阻塞任务,然后可以想到 Linux 的 select,epoll,简单搜索了下 Python 的 epoll,好像还有原生的包。

index.py - 通过 epoll 监听 stdin

import sys
import fcntl
import select
from mb import MB
import json

mbe = MB('./data')

# epoll 模型
fd = sys.stdin.fileno()
epoll = select.epoll()
epoll.register(fd, select.EPOLLIN)

try:
    while True:
        events = epoll.poll(10) # 同步非阻塞
        data = ''
        for fileno, event in events:
            data += sys.stdin.readline() # 通过标准输入获取数据
            if data == '' or data == '\n':
                continue
            items = xxx # 数处理过程
            for item in items:
                result = mbe.get(item)
                sys.stdout.write(json.dumps(result, ensure_ascii=False) +'\n') # 写入到标准输出
                sys.stdout.flush() # 缓冲区刷新
finally:
    epoll.unregister(fd)
    epoll.close()

index.js - 通过 stdin 发送数据

const child_process = require('child_process');
const child = child_process.spawn('python3', ['./base.py']);

let callbacks =  [], 
    chunks=Buffer.alloc(0), 
    chunkArr = [], 
    data = '', 
    onwork = false; // buffer 无法动态扩容
    
child.stdout.on('data', (chunk) => {
    chunkArr.push(chunk)
    if (onwork) return;
    onwork = true;
    while(chunkArr.length) {
        chunks = Buffer.concat([chunks, chunkArr.pop()]);
        const length = chunks.length;
        let trunkAt = -1;
        for(const [k, d] of chunks.entries()) {
            if (d == '0x0a') { // 0a 结尾
                data += chunks.slice(trunkAt+1, trunkAt=k);
                const cb = callbacks.shift();
                cb(null, data === 'null' ? null : data )
                data = '';
            }
        }
        if (trunkAt < length) {
            chunks = chunks.slice(trunkAt+1)
        }
    }
    onwork = false;
})

setInterval(() => {
    if (callbacks.length) child.stdin.write(`\n`); // Nodejs端的标准输入输出没有flush方法,只能 hack, 写入后python无法及时获取到最新
}, 500)

exports.getMsg = function getMsg(ip, cb) {
    callbacks.push(cb)
    child.stdin.write(`${ip}\n`); // 把数据写入到子进程的标准输入
}

Python 与 Nodejs 通过 stdio 实现通信; Python 通过 epoll 监听 stdin 实现驻留内存,长时间运行。

存在问题

  • Nodejs 把标准输出作为执行结果,故 Python 端只能把执行结果写入标准输出,不能有额外的打印信息
  • Nodejs 端标准输入没有 flush 方法,所以 Python 端事件触发不够及时,目前通过在Nodejs端定时发送空信息来 hack 实现
  • Buffer 没法动态扩容,没有C语言的指针好用,在解析 stdout 时写丑

总结

虽然可以实现 Nodejs 和 Python 的双向通信,然后由于上述种种问题,在这里并不推荐使用这种方式,通过 HTTP 或 Socket 方式比这个香多了。

到此这篇关于Nodejs与Python 双向通信的实现代码的文章就介绍到这了,更多相关Nodejs与Python双向通信内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js将字符串转成正则表达式的实现方法
Nov 13 Javascript
用JS在浏览器中创建下载文件
Mar 05 Javascript
当达到输入长度时表单自动切换焦点
Apr 06 Javascript
Jquery 实现checkbox全选方法
Jan 28 Javascript
javascript的switch用法注意事项分析
Feb 02 Javascript
js实现下拉框选择要显示图片的方法
Feb 16 Javascript
javascript中FOREACH数组方法使用示例
Mar 01 Javascript
原生js开发的日历插件
Feb 04 Javascript
Angularjs上传图片实例详解
Aug 06 Javascript
微信小程序实现折叠面板
Jan 31 Javascript
vue动态路由配置及路由传参的方式
May 23 Javascript
layui实现图片虚拟路径上传,预览和删除的例子
Sep 25 Javascript
node.js如何自定义实现一个EventEmitter
Jul 16 #Javascript
node.js使用express-fileupload中间件实现文件上传
Jul 16 #Javascript
html5 录制mp3音频支持采样率和比特率设置
js基础语法与maven项目配置教程案例
JavaScript与JQuery框架基础入门教程
Jul 15 #Javascript
Python机器学习之决策树和随机森林
微信小程序scroll-view不能左右滑动问题的解决方法
You might like
第八节 访问方式 [8]
2006/10/09 PHP
如何利用PHP实现上传图片功能详解
2020/09/24 PHP
用javascript实现计算两个日期的间隔天数
2007/08/14 Javascript
jquery中输入验证中一个不错的效果
2010/08/21 Javascript
jquery怎样实现ajax联动框(一)
2013/03/08 Javascript
引用外部js乱码问题分析及解决方案
2013/04/12 Javascript
基于Jquery+Ajax+Json实现分页显示附效果图
2014/07/30 Javascript
Javascript前端UI框架Kit使用指南之Kitjs简介
2014/11/28 Javascript
纯js实现无限空间大小的本地存储
2015/06/18 Javascript
js clearInterval()方法的定义和用法
2015/11/11 Javascript
基于jQuery实现页面搜索功能
2020/03/26 Javascript
d3.js中冷门却实用的内置函数总结
2017/02/04 Javascript
VSCode中如何利用d.ts文件进行js智能提示
2018/04/13 Javascript
element-ui 本地化使用教程详解
2019/10/28 Javascript
[48:29]2018DOTA2亚洲邀请赛3月30日 小组赛A组 LGD VS KG
2018/03/31 DOTA
python使用三角迭代计算圆周率PI的方法
2015/03/20 Python
Python检测QQ在线状态的方法
2015/05/09 Python
Python编程中实现迭代器的一些技巧小结
2016/06/21 Python
Python中str is not callable问题详解及解决办法
2017/02/10 Python
Python对象转换为json的方法步骤
2019/04/25 Python
python3 线性回归验证方法
2019/07/09 Python
jupyter notebook插入本地图片的实现
2020/04/13 Python
在ipython notebook中使用argparse方式
2020/04/20 Python
Python内存泄漏和内存溢出的解决方案
2020/09/26 Python
matplotlib教程——强大的python作图工具库
2020/10/15 Python
戴森美国官网:Dyson美国
2016/09/11 全球购物
喜诗官方在线巧克力店:See’s Candies
2017/01/01 全球购物
英国家庭和商业健身器材购物网站:Fitness Options
2018/07/05 全球购物
Currentbody澳大利亚:美容仪专家
2019/11/11 全球购物
什么是事务?为什么需要事务?
2012/01/09 面试题
linux系统都有哪些运行级别
2016/03/26 面试题
应届本科毕业生求职信
2014/07/23 职场文书
go语言求任意类型切片的长度操作
2021/04/26 Golang
详解Python自动化之文件自动化处理
2021/06/21 Python
Java生成读取条形码和二维码的简单示例
2021/07/09 Java/Android
TS 类型兼容教程示例详解
2022/09/23 Javascript