Node.jsは私たちのWebプロジェクトに完全に一致しますが、Pythonを好む計算タスクはほとんどありません。また、Pythonコードも既に用意されています。私たちは速度に非常に関心があります。非同期の非ブロッキング方法でnode.jsからPythonの「ワーカー」を呼び出す最もエレガントな方法は何ですか?
Node.jsは私たちのWebプロジェクトに完全に一致しますが、Pythonを好む計算タスクはほとんどありません。また、Pythonコードも既に用意されています。私たちは速度に非常に関心があります。非同期の非ブロッキング方法でnode.jsからPythonの「ワーカー」を呼び出す最もエレガントな方法は何ですか?
回答:
node.jsとPythonサーバー間の通信では、両方のプロセスが同じサーバーで実行されている場合はUnixソケットを使用し、そうでない場合はTCP / IPソケットを使用します。プロトコルのマーシャリングには、JSONまたはプロトコルバッファを使用します。スレッド化されたPythonがボトルネックであることが判明した場合は、Twisted Pythonの使用を検討してください。これにより、node.jsと同じイベント駆動型同時実行性が提供されます。
冒険したい場合は、clojure(clojurescript、clojure-py)を学ぶと、Java、JavaScript(node.jsを含む)、CLR、Pythonで既存のコードと実行および相互運用できる同じ言語が得られます。また、clojureデータ構造を使用するだけで、優れたマーシャリングプロトコルを利用できます。
これは、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");
そして、Pythonクライアント
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)
Apache Thrift http://thrift.apache.org/も検討します
複数のプログラミング言語間を橋渡しでき、効率が高く、非同期または同期呼び出しをサポートしています。ここで完全な機能を見るhttp://thrift.apache.org/docs/features/
多言語は将来の計画に役立ちます。たとえば、後でC ++で計算タスクの一部を実行したい場合、Thriftを使用してそれをミックスに追加するのは非常に簡単です。
thoonk.jsとthoonk.pyを使用することで、多くの成功を収めてきました。ThoonkはRedis(インメモリKey-Valueストア)を利用して、フィード(パブリッシュ/サブスクライブを考える)、キュー、および通信のジョブパターンを提供します。
なぜこれがUNIXソケットや直接TCPソケットよりも優れているのですか?全体的なパフォーマンスは少し低下する可能性がありますが、Thoonkはソケットを手動で処理する必要があることを簡素化する本当にシンプルなAPIを提供します。Thoonkは、Pythonワーカーの新しいインスタンスを起動して同じRedisサーバーに接続するだけなので、Pythonワーカーをスケーリングしてパフォーマンスを向上できる分散コンピューティングモデルを実装することを非常に簡単にするのにも役立ちます。
2019年更新
これを実現するにはいくつかの方法があります。ここに複雑さの昇順のリストを示します
アプローチ1 Python Shell最も単純なアプローチ
source.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);
})
destination.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()
注:source.jsファイルと同じレベルにあるsubscriberという名前のフォルダーを作成し、その中にdestination.pyを置きます。virtualenv環境を変更することを忘れないでください