Combinazione di node.js e Python


127

Node.js è una corrispondenza perfetta per il nostro progetto Web, ma ci sono alcune attività di calcolo per le quali preferiremmo Python. Abbiamo anche un codice Python per loro. Siamo molto preoccupati per la velocità, qual è il modo più elegante come chiamare un "lavoratore" Python da node.js in modo asincrono e non bloccante?


3
Ciao, potresti condividere con noi cosa hai scelto e come ha funzionato per te? Ci sono librerie in Python che tutti adoriamo usare mantenendo le prestazioni e le opzioni non bloccanti. Grazie
Maziyar

Che dire semplicemente di generare / fork un processo e comunicare attraverso l'IO di sistema, come suggerisce questo: sohamkamani.com/blog/2015/08/21/python-nodejs-comm ?
lkahtz,

Esiste una nuova libreria di bridge denominata PyNode che consente di chiamare Python e ottenere la restituzione dei tipi JS. È dimostrato qui thecodinginterface.com/blog/…
SciGuyMcQ

Risposte:


86

Per la comunicazione tra node.js e il server Python, utilizzerei i socket Unix se entrambi i processi fossero eseguiti sullo stesso server e socket TCP / IP in caso contrario. Per il protocollo di marshalling prenderei JSON o il buffer di protocollo . Se Python thread è un collo di bottiglia, considera l'utilizzo di Twisted Python , che fornisce la stessa concorrenza guidata dagli eventi di node.js.

Se ti senti avventuroso, impara clojure ( clojurescript , clojure-py ) e otterrai la stessa lingua che viene eseguita e interagisce con il codice esistente su Java, JavaScript (node.js incluso), CLR e Python. E ottieni un protocollo di smistamento eccezionale semplicemente usando le strutture di dati clojure.


2
Sai se qualcosa di simile funzionerà su Heroku, che ha un filesystem effimero?
cm2

119

Sembra uno scenario in cui zeroMQ sarebbe adatto. È un framework di messaggistica simile all'utilizzo dei socket TCP o Unix, ma è molto più robusto ( http://zguide.zeromq.org/py:all )

C'è una libreria che utilizza zeroMQ per fornire un framework RPC che funziona abbastanza bene. Si chiama zeroRPC ( http://www.zerorpc.io/ ). Ecco il mondo ciao.

Server "Hello x" Python:

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

E il client 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);
});

O viceversa, server 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");

E il client 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)

4
Zerorpc può gestire più stati in caso di più sessioni client?
user1027169

Buona risposta, esempi di esempio, spiegazione abbondante e cosa stavo cercando. TY. +1
Gaurav Gandhi

1
se sei nuovo come me, installa le dipendenze che menzionano qui - ianhinsdale.com/code/2013/12/08/…
Darpan

Grazie mille per questo!
Gezim,

1
Bel ciao demo mondiale! Un'altra soluzione simile di seguito con Rabbitmq. medium.com/@HolmesLaurence/...
teng

7

Se si organizza un lavoratore Python in un processo separato (processo di tipo server di lunga esecuzione o un figlio generato su richiesta), la comunicazione con esso sarà asincrona sul lato node.js. I socket UNIX / TCP e la comunicazione stdin / out / err sono intrinsecamente asincroni nel nodo.


6

Considererei anche Apache Thrift http://thrift.apache.org/

Può eseguire il bridge tra diversi linguaggi di programmazione, è altamente efficiente e supporta le chiamate asincrone o sincronizzate. Vedi le funzionalità complete qui http://thrift.apache.org/docs/features/

Il multi-linguaggio può essere utile per i piani futuri, ad esempio se in seguito si desidera svolgere parte dell'attività di calcolo in C ++, è molto semplice aggiungerlo al mix utilizzando Thrift.


5

Ho avuto molto successo usando thoonk.js insieme a thoonk.py . Thoonk sfrutta Redis (archivio di valori-chiave in memoria) per fornire feed (pensa a pubblicare / abbonarsi), code e modelli di lavoro per la comunicazione.

Perché è meglio di socket unix o socket tcp diretti? Le prestazioni complessive potrebbero essere leggermente ridotte, tuttavia Thoonk fornisce un'API davvero semplice che semplifica la gestione manuale di un socket. Thoonk aiuta anche a rendere davvero banale l'implementazione di un modello di calcolo distribuito che ti consente di ridimensionare i tuoi lavoratori Python per aumentare le prestazioni, dal momento che fai semplicemente girare nuove istanze dei tuoi lavoratori Python e collegali allo stesso server Redis.


3

Ti consiglierei di usare una coda di lavoro usando, ad esempio, l'ottimo Gearman , che ti fornirà un ottimo modo per inviare lavori in background e ottenere i loro risultati in modo asincrono una volta elaborati.

Il vantaggio di questo, ampiamente utilizzato in Digg (tra molti altri) è che fornisce un modo forte, scalabile e robusto per fare in modo che i lavoratori in qualsiasi lingua parlino con i clienti in qualsiasi lingua.


1

Aggiornamento 2019

Esistono diversi modi per raggiungere questo obiettivo ed ecco l'elenco in ordine crescente di complessità

  1. Python Shell, scriverai stream sulla console python e ti riscriverà
  2. Redis Pub Sub, puoi avere un canale in ascolto in Python mentre l'editore js del nodo invia i dati
  3. Connessione WebSocket in cui Nodo funge da client e Python funge da server o viceversa
  4. Connessione API con Express / Flask / Tornado ecc. Che funzionano separatamente con un endpoint API esposto per l'interrogazione dell'altro

Approccio 1 Python Shell Approccio più semplice

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

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

Note : crea una cartella chiamata subscriber che si trova allo stesso livello del file source.js e inserisci al suo interno destination.py. Non dimenticare di cambiare il tuo ambiente virtualenv

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.