web-dev-qa-db-ja.com

node.jsとPythonの組み合わせ

Node.jsは私たちのWebプロジェクトにぴったりですが、Pythonを好む計算タスクはほとんどありません。また、それらのPythonコードも既にあります。私たちは速度を非常に心配しています。node.jsからPython "worker"を非同期の非ブロッキング方法で呼び出す最もエレガントな方法は何ですか?

123
Cartesius00

Node.jsとPythonサーバー間の通信では、両方のプロセスが同じサーバーで実行される場合はUnixソケットを使用し、それ以外の場合はTCP/IPソケットを使用します。マーシャリングプロトコルの場合、JSONまたは プロトコルバッファー を使用します。スレッドPythonがボトルネックになっている場合は、 Twisted Python を使用することを検討してください。これは、node.jsと同じイベント駆動型の並行性を提供します。

冒険心があるなら、 clojureclojurescriptclojure-py )を学ぶと、既存のコードと実行および相互運用する同じ言語が得られますJava、JavaScript(node.jsを含む)、CLRおよびPython。また、clojureデータ構造を使用するだけで、優れたマーシャリングプロトコルを取得できます。

84
Aleš Kotnik

これは、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 __== "__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)
116
djheru

Pythonワーカーを別のプロセス(長時間実行されるサーバータイプのプロセスまたはオンデマンドで生成された子)に配置する場合、node.js側での通信は非同期になります。 UNIX/TCPソケットとstdin/out/err通信は、本質的にノード内で非同期です。

7
lanzz

Apache Thriftも検討します http://thrift.Apache.org/

複数のプログラミング言語間を橋渡しすることができ、非常に効率的で、非同期呼び出しまたは同期呼び出しをサポートしています。ここですべての機能を参照してください http://thrift.Apache.org/docs/features/

多言語は将来の計画に役立ちます。たとえば、後でC++で計算タスクの一部を実行したい場合は、Thriftを使用して簡単に追加できます。

6
Iftah

thoonk.js とともに thoonk.py を使用して、多くの成功を収めました。 Thoonkは、Redis(インメモリキーバリューストア)を活用して、通信のフィード(パブリッシュ/サブスクライブを考える)、キュー、ジョブパターンを提供します。

UNIXソケットや直接TCPソケットよりも優れているのはなぜですか?全体的なパフォーマンスは少し低下する可能性がありますが、Thoonkは、ソケットを手動で処理する必要を簡素化する非常にシンプルなAPIを提供します。 Thoonkは、pythonワーカーの新しいインスタンスをスピンアップして接続するだけなので、pythonワーカーをスケーリングしてパフォーマンスを向上させる分散コンピューティングモデルを実装することも非常に簡単になります。それらを同じredisサーバーに追加します。

5
Doug McCall

たとえば、優れた Gearman を使用していくつかのワークキューを使用することをお勧めします。これにより、バックグラウンドジョブをディスパッチし、処理後に非同期で結果を取得するための優れた方法が提供されます。

Diggで(他の多くの中でも)頻繁に使用されるこの利点は、あらゆる言語の従業員があらゆる言語のクライアントと話すことができる、強力でスケーラブルで堅牢な方法を提供することです。

3
Pierre

更新2019

これを実現するにはいくつかの方法がありますが、ここに複雑さの昇順のリストがあります

  1. Pythonシェル、ストリームをpythonコンソールに書き込み、それを元に戻します
  2. Redis Pub Sub、ノードjsパブリッシャーがデータをプッシュしている間にPythonでリッスンしているチャンネルを持つことができます
  3. Nodeがクライアントとして機能し、Pythonがサーバーとして機能するWebsocket接続、またはその逆
  4. Express/Flask/TornadoなどとのAPI接続は、他のクエリ用に公開されたAPIエンドポイントとは別に動作

アプローチ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環境を変更することを忘れないでください

0
PirateApp