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、jquery图片动画、动态切换示例代码
Jun 03 Javascript
jquery 实现返回顶部功能
Nov 17 Javascript
JQuery控制div外点击隐藏而div内点击不会隐藏的方法
Jan 13 Javascript
Javascript数组操作函数总结
Feb 05 Javascript
动态创建按钮的JavaScript代码
Jan 29 Javascript
Vue实现双向绑定的原理以及响应式数据的方法
Jul 02 Javascript
微信小程序中换行空格(多个空格)写法详解
Jul 10 Javascript
JavaScript代码压缩工具UglifyJS和Google Closure Compiler的基本用法
Apr 13 Javascript
微信小程序scroll-view实现滚动到锚点左侧导航栏点餐功能(点击种类,滚动到锚点)
Jun 11 Javascript
Vue两种组件类型:递归组件和动态组件的用法
Aug 06 Javascript
Vue在H5 项目中使用融云进行实时个人单聊通讯
Dec 14 Vue.js
vue中watch的用法汇总
Dec 28 Vue.js
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 万年历实现代码
2012/10/18 PHP
基于Zookeeper的使用详解
2013/05/02 PHP
解析php中的escape函数
2013/06/29 PHP
php像数组一样存取和修改字符串字符
2014/03/21 PHP
destoon二次开发常用数据库操作
2014/06/21 PHP
php实现监控varnish缓存服务器的状态
2014/12/30 PHP
php遍历删除整个目录及文件的方法
2015/03/13 PHP
php处理json格式数据经典案例总结
2016/05/19 PHP
Node.js开源应用框架HapiJS介绍
2015/01/14 Javascript
jQuery+ajax实现无刷新级联菜单示例
2015/05/21 Javascript
javascript引用类型之时间Date和数组Array
2015/08/27 Javascript
jQuery+Ajax+PHP+Mysql实现分页显示数据实例讲解
2015/09/27 Javascript
vue.js初学入门教程(1)
2016/11/03 Javascript
JS限定手机版中图片大小随分辨率自动调整的方法
2016/12/05 Javascript
学好js,这些js函数概念一定要知道【推荐】
2017/01/19 Javascript
用vue封装插件并发布到npm的方法步骤
2017/10/18 Javascript
JS+WCF实现进度条实时监测数据加载量的方法详解
2017/12/19 Javascript
解决vue中使用Axios调用接口时出现的ie数据处理问题
2018/08/13 Javascript
微信小程序实现Swiper轮播图效果
2019/11/22 Javascript
微信小程序自定义底部弹出框动画
2020/11/18 Javascript
virtualenv 指定 python 解释器的版本方法
2018/10/25 Python
Python简易版图书管理系统
2019/08/12 Python
OpenCV Python实现拼图小游戏
2020/03/23 Python
python使用scapy模块实现ping扫描的过程详解
2021/01/21 Python
FC-Moto瑞典:欧洲最大的摩托车服装和头盔商店之一
2018/11/27 全球购物
牧马人澳大利亚官网:Wrangler澳大利亚
2019/10/08 全球购物
Viking Direct爱尔兰:办公用品和家具
2019/11/21 全球购物
迪奥美国官网:Dior美国
2019/12/07 全球购物
工程类专业自荐信范文
2014/03/09 职场文书
法律进学校实施方案
2014/03/15 职场文书
闭幕式主持词
2014/04/02 职场文书
小学校长竞聘演讲稿
2014/05/16 职场文书
2014年业务工作总结
2014/11/17 职场文书
法人代表证明书范本
2015/06/18 职场文书
2015年教师党员个人总结
2015/11/24 职场文书
LayUI+Shiro实现动态菜单并记住菜单收展的示例
2021/05/06 Javascript