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 相关文章推荐
JavaScript instanceof 的使用方法示例介绍
Oct 23 Javascript
13 款最热门的 jQuery 图像 360 度旋转插件推荐
Dec 09 Javascript
JQuery中extend的用法实例分析
Feb 08 Javascript
js读取并解析JSON类型数据的方法
Nov 14 Javascript
js实现多图左右切换功能
Aug 04 Javascript
jQuery实现手机版页面翻页效果的简单实例
Oct 05 Javascript
bootstrap监听滚动实现头部跟随滚动
Nov 08 Javascript
详解vue2.0 使用动态组件实现 Tab 标签页切换效果(vue-cli)
Aug 30 Javascript
基于JSON数据格式详解
Aug 31 Javascript
Node.js+ELK日志规范的实现
May 23 Javascript
jQuery擦除插件eraser使用方法详解
Jan 11 jQuery
vue 解决provide和inject响应的问题
Nov 12 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
PHP新手上路(十一)
2006/10/09 PHP
ThinkPHP使用getlist方法实现数据搜索功能示例
2017/05/08 PHP
ExtJs 表单提交登陆实现代码
2010/08/19 Javascript
基于Jquery实现表格动态分页实现代码
2011/06/21 Javascript
js 获取和设置css3 属性值的实现方法
2013/05/06 Javascript
JavaScript代码简单实现求杨辉三角给定行的最大值
2013/10/29 Javascript
ajax提交表单实现网页无刷新注册示例
2014/05/08 Javascript
Javascript中获取对象的原型对象的方法小结
2015/02/25 Javascript
AngularJS 路由和模板实例及路由地址简化方法(必看)
2016/06/24 Javascript
Bootstrap基本样式学习笔记之表格(2)
2016/12/07 Javascript
详解webpack 入门与解析
2018/04/09 Javascript
利用chrome浏览器进行js调试并找出元素绑定的点击事件详解
2021/01/30 Javascript
浅谈vue单页面中有多个echarts图表时的公用代码写法
2020/07/19 Javascript
VSCode插件安装完成后的配置(常用配置)
2020/08/24 Javascript
在vue中获取wangeditor的html和text的操作
2020/10/23 Javascript
Python处理JSON时的值报错及编码报错的两则解决实录
2016/06/26 Python
解决python 自动安装缺少模块的问题
2018/10/22 Python
Django models.py应用实现过程详解
2019/07/29 Python
matplotlib命令与格式之tick坐标轴日期格式(设置日期主副刻度)
2019/08/06 Python
python全局变量引用与修改过程解析
2020/01/07 Python
分享全球十款超强HTML5开发工具
2014/05/14 HTML / CSS
a标签下载链接的简单实现
2016/09/13 HTML / CSS
荷兰包包购物网站:The Little Green Bag
2018/03/17 全球购物
早晨薰衣草在线女性精品店:Morning Lavender
2021/01/04 全球购物
畜牧兽医本科生个人的自我评价
2013/10/11 职场文书
简单的辞职信范文
2014/01/18 职场文书
小学生期末自我鉴定
2014/01/19 职场文书
模特职业生涯规划范文
2014/02/26 职场文书
销售个人求职信范文
2014/04/28 职场文书
法院授权委托书格式
2014/09/28 职场文书
开票员岗位职责
2015/02/12 职场文书
pycharm代码删除恢复的方法
2021/06/26 Python
mysql中int(3)和int(10)的数值范围是否相同
2021/10/16 MySQL
springboot新建项目pom.xml文件第一行报错的解决
2022/01/18 Java/Android
漫画《尖帽子的魔法工坊》宣布动画化
2022/04/06 日漫
springboot创建的web项目整合Quartz框架的项目实践
2022/06/21 Java/Android