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 相关文章推荐
JQuyer $.post 与 $.ajax 访问WCF ajax service 时的问题需要注意的地方
Sep 20 Javascript
php跨域调用json的例子
Nov 13 Javascript
JS中类或对象的定义说明
Mar 10 Javascript
jquery原创弹出层折叠效果点击折叠弹出一个层
Mar 12 Javascript
JS实现简单的顶部定时关闭层效果
Jun 15 Javascript
JS+CSS实现美化的下拉列表框效果
Aug 11 Javascript
bootstrap-treeview自定义双击事件实现方法
Jan 09 Javascript
BootStrap Table后台分页时前台删除最后一页所有数据refresh刷新后无数据问题
Dec 28 Javascript
JS中的事件委托实例浅析
Mar 22 Javascript
Angular resolve基础用法详解
Oct 03 Javascript
JavaScript实现好看的跟随彩色气泡效果
Feb 06 Javascript
JavaScript实时更新当前的时间的示例代码
Jul 15 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
《Re:从零开始的异世界生活 冰结之绊》
2020/04/09 日漫
example2.php
2006/10/09 PHP
php+mysql实现无限级分类 | 树型显示分类关系
2006/11/19 PHP
php性能优化分析工具XDebug 大型网站调试工具
2011/05/22 PHP
PHP异步调用socket实现代码
2012/01/12 PHP
深入Memcache的Session数据的多服务器共享详解
2013/06/13 PHP
浅谈PHP变量作用域以及地址引用问题
2013/12/27 PHP
Laravel中使用阿里云OSS Composer包分享
2015/02/10 PHP
再谈javascript图片预加载技术(详细演示)
2011/03/12 Javascript
浅析jQuery中常用的元素查找方法总结
2013/07/04 Javascript
jquery中animate动画积累的解决方法
2013/10/05 Javascript
基于jquery的simpleValidate简易验证插件
2014/01/31 Javascript
node.js中的fs.chown方法使用说明
2014/12/16 Javascript
jQuery实现精美的多级下拉菜单特效
2015/03/14 Javascript
vue2.0实战之使用vue-cli搭建项目(2)
2017/03/27 Javascript
解决bootstrap模态框数据缓存的问题方法
2018/08/10 Javascript
详解如何构建Promise队列实现异步函数顺序执行
2018/10/23 Javascript
vue使用swiper实现中间大两边小的轮播图效果
2019/11/24 Javascript
Vue.js获取手机系统型号、版本、浏览器类型的示例代码
2020/05/10 Javascript
带你了解python装饰器
2017/06/15 Python
如何高效使用Python字典的方法详解
2017/08/31 Python
TensorFlow搭建神经网络最佳实践
2018/03/09 Python
Python图像处理之识别图像中的文字(实例讲解)
2018/05/10 Python
[原创]Python入门教程2. 字符串基本操作【运算、格式化输出、常用函数】
2018/10/29 Python
Python功能点实现:函数级/代码块级计时器
2019/01/02 Python
python实现视频分帧效果
2019/05/31 Python
解决python中用matplotlib画多幅图时出现图形部分重叠的问题
2019/07/07 Python
从零开始的TensorFlow+VScode开发环境搭建的步骤(图文)
2020/08/31 Python
Python安装第三方库攻略(pip和Anaconda)
2020/10/15 Python
简单总结CSS3中视窗单位Viewport的常见用法
2016/02/04 HTML / CSS
英国DVD和蓝光碟片购买网站:Zoom.co.uk(电影和电视)
2019/09/23 全球购物
党员党性分析材料
2014/02/17 职场文书
《动手做做看》教学反思
2014/04/09 职场文书
交通事故调解协议书
2014/04/16 职场文书
离婚被告代理词
2015/05/23 职场文书
JavaScript原型链详解
2021/11/07 Javascript