Combiner node.js et Python

127

Node.js correspond parfaitement à notre projet Web, mais il existe peu de tâches de calcul pour lesquelles nous préférerions Python. Nous avons également déjà un code Python pour eux. Nous sommes très préoccupés par la vitesse, quelle est la manière la plus élégante d'appeler un "worker" Python à partir de node.js de manière asynchrone et non bloquante?

Cartesius00
la source
3
Bonjour, pourriez-vous nous dire ce que vous avez choisi et comment cela a fonctionné pour vous? Il existe des bibliothèques en Python que nous aimons tous utiliser tout en conservant les performances et les options non bloquantes. Merci
Maziyar
Qu'en est-il simplement de générer / fourcher un processus et de communiquer via le système IO, comme celui-ci le suggère: sohamkamani.com/blog/2015/08/21/python-nodejs-comm ?
lkahtz le
Il existe une nouvelle bibliothèque de pontage nommée PyNode qui vous permet d'appeler Python et d'obtenir des types JS renvoyés. C'est démontré ici thecodinginterface.com/blog/…
SciGuyMcQ

Réponses:

86

Pour la communication entre node.js et le serveur Python, j'utiliserais des sockets Unix si les deux processus s'exécutaient sur le même serveur et les sockets TCP / IP sinon. Pour le protocole de marshaling, je prendrais JSON ou un tampon de protocole . Si Python threadé s'avère être un goulot d'étranglement, envisagez d'utiliser Twisted Python , qui fournit la même concurrence d'accès pilotée par les événements que node.js.

Si vous vous sentez aventureux, apprenez clojure ( clojurescript , clojure-py ) et vous obtiendrez le même langage qui s'exécute et interagit avec le code existant sur Java, JavaScript (node.js inclus), CLR et Python. Et vous obtenez un superbe protocole de marshalling en utilisant simplement des structures de données clojure.

Aleš Kotnik
la source
2
Savez-vous si quelque chose comme ça fonctionnera sur Heroku, qui a un système de fichiers éphémère?
cm2
119

Cela ressemble à un scénario où zeroMQ serait un bon ajustement. C'est un framework de messagerie similaire à l'utilisation de sockets TCP ou Unix, mais il est beaucoup plus robuste ( http://zguide.zeromq.org/py:all )

Il existe une bibliothèque qui utilise zeroMQ pour fournir un framework RPC qui fonctionne plutôt bien. Il s'appelle zeroRPC ( http://www.zerorpc.io/ ). Voici le bonjour le monde.

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

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

Ou vice-versa, serveur 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");

Et le 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)
Djheru
la source
4
Zerorpc peut-il gérer plusieurs états en cas de sessions client multiples?
user1027169
Bonne réponse, exemples d'exemples, explications abondantes et ce que je cherchais. TY. +1
Gaurav Gandhi
1
si vous êtes nouveau comme moi, installez les dépendances mentionnées ici - ianhinsdale.com/code/2013/12/08
Darpan
Merci beaucoup pour cela!
Gezim
1
Belle démo bonjour du monde! Une autre solution similaire ci-dessous en utilisant Rabbitmq. medium.com/@HolmesLaurence/…
teng
7

Si vous décidez d'avoir votre worker Python dans un processus séparé (soit un processus de type serveur de longue durée, soit un enfant généré à la demande), votre communication avec lui sera asynchrone du côté node.js. Les sockets UNIX / TCP et la communication stdin / out / err sont intrinsèquement asynchrones dans le nœud.

Lanzz
la source
6

Je considérerais également Apache Thrift http://thrift.apache.org/

Il peut faire le pont entre plusieurs langages de programmation, est très efficace et prend en charge les appels asynchrones ou synchronisés. Voir toutes les fonctionnalités ici http://thrift.apache.org/docs/features/

Le multi langage peut être utile pour les projets futurs, par exemple si vous souhaitez plus tard faire une partie de la tâche de calcul en C ++, il est très facile de l'ajouter au mix en utilisant Thrift.

Iftah
la source
5

J'ai eu beaucoup de succès en utilisant thoonk.js avec thoonk.py . Thoonk exploite Redis (magasin de valeurs-clés en mémoire) pour vous fournir des modèles de flux (pensez publier / souscrire), de files d'attente et de tâches pour la communication.

Pourquoi est-ce mieux que les sockets Unix ou les sockets TCP directs? Les performances globales peuvent être légèrement diminuées, cependant Thoonk fournit une API très simple qui simplifie le traitement manuel d'une socket. Thoonk contribue également à rendre très simple la mise en œuvre d'un modèle de calcul distribué qui vous permet de mettre à l'échelle vos nœuds de calcul python pour augmenter les performances, car il vous suffit de lancer de nouvelles instances de vos nœuds de calcul python et de les connecter au même serveur redis.

Doug McCall
la source
3

Je recommanderais d'utiliser une file d'attente de travail en utilisant, par exemple, l'excellent Gearman , qui vous fournira un excellent moyen de répartir les tâches en arrière-plan et d'obtenir leur résultat de manière asynchrone une fois qu'elles sont traitées.

L'avantage de cela, largement utilisé chez Digg (parmi beaucoup d'autres), est qu'il fournit un moyen solide, évolutif et robuste de permettre aux travailleurs de n'importe quelle langue de parler avec les clients dans n'importe quelle langue.

Pierre
la source
1

Mise à jour 2019

Il existe plusieurs façons d'y parvenir et voici la liste par ordre croissant de complexité

  1. Python Shell, vous écrirez des flux sur la console python et il vous répondra
  2. Redis Pub Sub, vous pouvez avoir une chaîne à l'écoute en Python pendant que votre éditeur node js pousse des données
  3. Connexion Websocket où Node agit en tant que client et Python agit en tant que serveur ou vice-versa
  4. Connexion API avec Express / Flask / Tornado, etc. fonctionnant séparément avec un point de terminaison API exposé pour que l'autre interroge

Approche 1 Python Shell Approche la plus simple

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

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

Notes : Créez un dossier appelé abonné qui est au même niveau que le fichier source.js et mettez destination.py à l'intérieur. N'oubliez pas de changer votre environnement virtualenv

PirateApp
la source