Kombination von node.js und Python


127

Node.js passt perfekt zu unserem Webprojekt, aber es gibt nur wenige Rechenaufgaben, für die wir Python bevorzugen würden. Wir haben auch bereits einen Python-Code für sie. Wir sind sehr besorgt über die Geschwindigkeit. Was ist die eleganteste Art, einen Python-Worker von node.js asynchron und nicht blockierend aufzurufen?


3
Hallo, können Sie uns mitteilen, was Sie ausgewählt haben und wie es für Sie funktioniert hat? Es gibt Bibliotheken in Python, die wir alle gerne verwenden, während die Leistung und die nicht blockierenden Optionen erhalten bleiben. Vielen Dank
Maziyar

Wie wäre es, einfach einen Prozess zu spawnen / zu verzweigen und über System- E / A zu kommunizieren, wie dies hier vorgeschlagen wird: sohamkamani.com/blog/2015/08/21/python-nodejs-comm ?
lkahtz

Es gibt eine neue Bridging-Bibliothek namens PyNode, mit der Sie Python aufrufen und JS-Typen zurückgeben können. Es wird hier demonstriert thecodinginterface.com/blog/…
SciGuyMcQ

Antworten:


86

Für die Kommunikation zwischen node.js und Python-Server würde ich Unix-Sockets verwenden, wenn beide Prozesse auf demselben Server ausgeführt werden, andernfalls TCP / IP-Sockets. Für das Marshalling-Protokoll würde ich JSON oder Protokollpuffer verwenden . Wenn sich Thread-Python als Engpass herausstellt , sollten Sie Twisted Python verwenden , das dieselbe ereignisgesteuerte Parallelität bietet wie node.js.

Wenn Sie sich abenteuerlustig fühlen, lernen Sie Clojure ( Clojurescript , Clojure-py ) und Sie erhalten dieselbe Sprache, die mit vorhandenem Code auf Java, JavaScript (einschließlich node.js), CLR und Python ausgeführt wird und zusammenarbeitet. Und Sie erhalten ein hervorragendes Marshalling-Protokoll, indem Sie einfach Clojure-Datenstrukturen verwenden.


2
Wissen Sie, ob so etwas auf Heroku funktioniert, das über ein kurzlebiges Dateisystem verfügt?
cm2

119

Dies klingt nach einem Szenario, in dem zeroMQ gut passt. Es ist ein Messaging-Framework, das der Verwendung von TCP- oder Unix-Sockets ähnelt, aber viel robuster ist ( http://zguide.zeromq.org/py:all ).

Es gibt eine Bibliothek, die zeroMQ verwendet, um ein RPC-Framework bereitzustellen, das ziemlich gut funktioniert. Es heißt zeroRPC ( http://www.zerorpc.io/ ). Hier ist die Hallo Welt.

Python "Hallo x" Server:

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()

Und der node.js-Client:

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);
});

Oder umgekehrt, node.js Server:

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");

Und der Python-Client

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)

4
Kann zerorpc mehrere Zustände verarbeiten, wenn mehrere Client-Sitzungen vorliegen?
user1027169

Gute Antwort, Beispielbeispiele, reichlich Erklärung und wonach ich gesucht habe. TY. +1
Gaurav Gandhi

1
Wenn Sie neu wie ich sind, installieren Sie die hier genannten Abhängigkeiten - ianhinsdale.com/code/2013/12/08/…
Darpan

Vielen Dank dafür!
Gezim

1
Schöne Hallo Welt Demo! Eine andere ähnliche Lösung unten unter Verwendung von Rabbitmq. medium.com/@HolmesLaurence/…
teng

7

Wenn Sie festlegen, dass Ihr Python-Worker in einem separaten Prozess (entweder einem lang laufenden Server-Prozess oder einem bei Bedarf erstellten Kind) ausgeführt wird, erfolgt Ihre Kommunikation mit ihm auf der Seite node.js asynchron. UNIX / TCP-Sockets und stdin / out / err-Kommunikation sind im Knoten von Natur aus asynchron.


6

Ich würde auch Apache Thrift in Betracht ziehen http://thrift.apache.org/

Es kann mehrere Programmiersprachen überbrücken, ist hocheffizient und unterstützt asynchrone oder synchronisierte Aufrufe. Die vollständigen Funktionen finden Sie hier http://thrift.apache.org/docs/features/

Die Mehrsprachigkeit kann für zukünftige Pläne nützlich sein. Wenn Sie beispielsweise später einen Teil der Rechenaufgabe in C ++ ausführen möchten, können Sie sie ganz einfach mit Thrift zum Mix hinzufügen.


5

Ich habe viel Erfolg mit thoonk.js zusammen mit thoonk.py gehabt . Thoonk nutzt Redis (speicherinterner Schlüsselwertspeicher), um Ihnen Feed- (Think Publish / Subscribe), Warteschlangen- und Jobmuster für die Kommunikation bereitzustellen.

Warum ist das besser als Unix-Sockets oder direkte TCP-Sockets? Die Gesamtleistung kann etwas abnehmen, Thoonk bietet jedoch eine wirklich einfache API, die es einfacher macht, manuell mit einem Socket umzugehen. Thoonk macht es auch sehr einfach, ein verteiltes Computermodell zu implementieren, mit dem Sie Ihre Python-Worker skalieren können, um die Leistung zu steigern, da Sie einfach neue Instanzen Ihrer Python-Worker hochfahren und sie mit demselben Redis-Server verbinden.


3

Ich würde empfehlen, eine Arbeitswarteschlange zu verwenden, indem Sie beispielsweise den hervorragenden Gearman verwenden , mit dem Sie Hintergrundjobs auf hervorragende Weise versenden und deren Ergebnis nach der Verarbeitung asynchron abrufen können.

Der Vorteil, der bei Digg (unter anderem) häufig verwendet wird, besteht darin, dass es eine starke, skalierbare und robuste Möglichkeit bietet, Mitarbeiter in jeder Sprache dazu zu bringen, mit Kunden in jeder Sprache zu sprechen.


1

Update 2019

Es gibt verschiedene Möglichkeiten, dies zu erreichen, und hier ist die Liste in aufsteigender Reihenfolge der Komplexität

  1. Python Shell, Sie werden Streams in die Python-Konsole schreiben und sie wird Ihnen zurückschreiben
  2. Redis Pub Sub, Sie können einen Kanal in Python abhören lassen, während der Herausgeber Ihres Knotens Daten pusht
  3. Websocket-Verbindung, bei der Node als Client und Python als Server fungiert oder umgekehrt
  4. API-Verbindung mit Express / Flask / Tornado usw., die separat mit einem API-Endpunkt arbeitet, der für den anderen zur Abfrage verfügbar gemacht wird

Ansatz 1 Python Shell Einfachster Ansatz

Datei 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);
})

Datei 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()

Hinweise : Erstellen Sie einen Ordner mit dem Namen "Abonnent", der sich auf derselben Ebene wie die Datei "source.js" befindet, und legen Sie "destination.py" darin ab. Vergessen Sie nicht, Ihre virtuelle Umgebung zu ändern

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.