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 相关文章推荐
基于jQuery图片平滑连续滚动插件
Apr 27 Javascript
深入理解Javascript作用域与变量提升
Dec 09 Javascript
js返回上一页并刷新的多种实现方法
Feb 26 Javascript
node.js正则表达式获取网页中所有链接的代码实例
Jun 03 Javascript
JavaScript使用ActiveXObject访问Access和SQL Server数据库
Apr 02 Javascript
JS比较两个数值的大小实例
Nov 25 Javascript
一篇文章搞定JavaScript类型转换(面试常见)
Jan 21 Javascript
javascript表达式和运算符详解
Feb 07 Javascript
微信小程序本地缓存数据增删改查实例详解
May 24 Javascript
react高阶组件添加和删除props
Apr 26 Javascript
Vue实现导航栏菜单
Aug 19 Javascript
JS访问对象两种方式区别解析
Aug 29 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用户指南-cookies部分
2006/10/09 PHP
php 页面执行时间计算代码
2008/12/04 PHP
PHP采集相关教程之一 CURL函数库
2010/02/15 PHP
js限制checkbox勾选的个数以及php获取多个checkbbox的方法深入解析
2013/07/18 PHP
yii实现CheckBox复选框在同一行显示的方法
2014/12/03 PHP
php站内搜索关键词变亮的实现方法
2014/12/30 PHP
jQuery formValidator表单验证插件开源了 含API帮助、源码、示例
2008/08/14 Javascript
JQuery 风格的HTML文本转义
2009/07/01 Javascript
JS 面向对象的5钟写法
2009/07/31 Javascript
JS表的模拟方法
2015/02/05 Javascript
jQuery点缩略图弹出层显示大图片
2015/02/13 Javascript
JavaScript对数组进行随机重排的方法
2015/07/22 Javascript
Javascript中的数据类型之旅
2015/10/18 Javascript
原生javascript实现图片放大镜效果
2017/01/18 Javascript
js事件冒泡与事件捕获详解
2017/02/20 Javascript
基于jQuery实现瀑布流页面
2017/04/11 jQuery
jQuery图片查看插件Magnify开发详解
2017/12/25 jQuery
Node.js搭建WEB服务器的示例代码
2018/08/15 Javascript
微信小程序遍历Echarts图表实现多个饼图
2019/04/25 Javascript
阿望教你用vue写扫雷小游戏
2020/01/20 Javascript
Python操作json数据的一个简单例子
2014/04/17 Python
浅析Python中的join()方法的使用
2015/05/19 Python
浅谈使用Python内置函数getattr实现分发模式
2018/01/22 Python
Python Flask框架扩展操作示例
2019/05/03 Python
Python进程间通信Queue消息队列用法分析
2019/05/22 Python
PyQt5下拉式复选框QComboCheckBox的实例
2019/06/25 Python
Python实现K折交叉验证法的方法步骤
2019/07/11 Python
Python 多线程,threading模块,创建子线程的两种方式示例
2019/09/29 Python
python如何使用腾讯云发送短信
2020/09/17 Python
Python+unittest+requests+excel实现接口自动化测试框架
2020/12/23 Python
英国著名音像制品和图书游戏购物网站:Zavvi
2016/08/04 全球购物
《胖乎乎的小手》教学反思
2014/02/26 职场文书
优秀会计求职信
2014/07/04 职场文书
就业协议书样本
2014/08/20 职场文书
2014年英语教研组工作总结
2014/12/06 职场文书
CSS作用域(样式分割)的使用汇总
2021/11/07 HTML / CSS