结合 node.js 和 Python

Js 非常适合我们的 Web 项目,但是对于一些计算任务,我们更喜欢 Python。我们已经为他们准备了 Python 代码。 我们非常关心速度,如何以异步非阻塞方式从 node.js 调用 Python“ worker”,最优雅的方式是什么?

118194 次浏览

如果您安排将 Python worker 放在一个单独的进程中(可以是长时间运行的服务器类型的进程,也可以是随需产生的子进程) ,那么与它的通信将在 node.js 端是异步的。UNIX/TCP 套接字和 stdin/out/err 通信在节点中本质上是异步的。

对于 node.js 和 Python 服务器之间的通信,如果两个进程运行在相同的服务器和 TCP/IP 套接字上,那么我将使用 Unix 套接字。对于封送处理协议,我将使用 JSON 或 协议缓冲区。如果线程化 Python 成为瓶颈,可以考虑使用 扭曲巨蟒,其 提供与 node.js 相同的事件驱动并发。

如果你喜欢冒险,学习 神灵(ClojurescriptClojure-py) ,你会得到与 Java、 JavaScript (包括 node.js)、 CLR 和 Python 上现有代码相同的运行和互操作语言。通过简单地使用 clojure 数据结构,您可以获得极好的编组协议。

我建议使用一些工作队列,例如使用优秀的 Gearman,它将为您提供分派后台作业的好方法,并在处理后异步获取它们的结果。

Digg (以及其他许多应用程序)广泛使用的这种方法的优势在于,它提供了一种强大的、可扩展的、健壮的方法,使任何语言的工作人员都可以用任何语言与客户交谈。

我在使用 Thonk.jsThonk.py时取得了很大的成功。Thonk 利用 Redis (内存中的键值存储)为通信提供 feed (考虑发布/订阅)、队列和作业模式。

为什么这比 unix 套接字或直接 tcp 套接字好?总体性能可能会有所下降,但是 Thonk 提供了一个非常简单的 API,可以简化手动处理套接字的过程。Thonk 还使实现一个分布式计算模型变得非常简单,该模型允许扩展 python 工作者以提高性能,因为您只需旋转 python 工作者的新实例并将它们连接到相同的 redis 服务器。

我也会考虑阿帕奇节俭 http://thrift.apache.org/

它可以在多种编程语言之间建立桥梁,效率很高,并且支持异步或同步调用。在这里看到完整的功能 http://thrift.apache.org/docs/features/

这种多语言对于未来的计划很有用,例如,如果你以后想用 C + + 完成一部分计算任务,那么很容易就可以使用 Thrift 将其添加到混合计算中。

这听起来像是 zeroMQ 非常适合的场景。它是一个类似于使用 TCP 或 Unix 套接字的消息传递框架,但是它更加健壮(http://zguide.zeromq.org/py:all)

有一个使用 zeroMQ 的库提供了一个运行良好的 RPC 框架。它叫做 zeroRPC (http://www.zerorpc.io/)。这就是你好的世界。

Python“ Hello x”服务器:

import zerorpc


class HelloRPC(object):
'''pass the method a name, it replies "Hello name!"'''
def hello(self, name):
return "Hello, {0}!".format(name)


def main():
s = zerorpc.Server(HelloRPC())
s.bind("tcp://*:4242")
s.run()


if __name__ == "__main__" : main()

还有 node.js 客户端:

var zerorpc = require("zerorpc");


var client = new zerorpc.Client();
client.connect("tcp://127.0.0.1:4242");
//calls the method on the python object
client.invoke("hello", "World", function(error, reply, streaming) {
if(error){
console.log("ERROR: ", error);
}
console.log(reply);
});

反之亦然,node.js 服务器:

var zerorpc = require("zerorpc");


var server = new zerorpc.Server({
hello: function(name, reply) {
reply(null, "Hello, " + name, false);
}
});


server.bind("tcp://0.0.0.0:4242");

还有巨蟒客户端

import zerorpc, sys


c = zerorpc.Client()
c.connect("tcp://127.0.0.1:4242")
name = sys.argv[1] if len(sys.argv) > 1 else "dude"
print c.hello(name)

2019年最新情况

有几种方法可以实现这一点,下面是按复杂程度递增的列表

  1. Python Shell,您将向 Python 控制台和它的 会给你回信的
  2. 红迪斯潜艇酒吧,你可以有一个频道 当您的节点 js 发布者推送数据时,您可以使用 Python 监听
  3. Websocket 连接,其中 Node 充当客户端,Python 充当客户端 作为服务器或反之亦然
  4. API 连接与 Express/Flask/Torado 等分开工作,API 端点公开供对方查询

方法1 Python Shell 最简单的方法

Js 文件

const ps = require('python-shell')
// very important to add -u option since our python script runs infinitely
var options = {
pythonPath: '/Users/zup/.local/share/virtualenvs/python_shell_test-TJN5lQez/bin/python',
pythonOptions: ['-u'], // get print results in real-time
// make sure you use an absolute path for scriptPath
scriptPath: "./subscriber/",
// args: ['value1', 'value2', 'value3'],
mode: 'json'
};


const shell = new ps.PythonShell("destination.py", options);


function generateArray() {
const list = []
for (let i = 0; i < 1000; i++) {
list.push(Math.random() * 1000)
}
return list
}


setInterval(() => {
shell.send(generateArray())
}, 1000);


shell.on("message", message => {
console.log(message);
})

Py 文件

import datetime
import sys
import time
import numpy
import talib
import timeit
import json
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)


size = 1000
p = 100
o = numpy.random.random(size)
h = numpy.random.random(size)
l = numpy.random.random(size)
c = numpy.random.random(size)
v = numpy.random.random(size)


def get_indicators(values):
# Return the RSI of the values sent from node.js
numpy_values = numpy.array(values, dtype=numpy.double)
return talib.func.RSI(numpy_values, 14)


for line in sys.stdin:
l = json.loads(line)
print(get_indicators(l))
# Without this step the output may not be immediately available in node
sys.stdout.flush()

注意 : 创建一个名为 scriber 的文件夹,该文件夹的级别与 source.js 文件相同,并将 destination.py 放在其中。不要忘记改变你的虚拟环境