Combinando node.js e Python

127

O Node.js é uma combinação perfeita para o nosso projeto web, mas há poucas tarefas computacionais para as quais preferimos o Python. Também já temos um código Python para eles. Estamos muito preocupados com a velocidade, qual é a maneira mais elegante de como chamar um "trabalhador" Python do node.js de maneira assíncrona e sem bloqueio?

Cartesius00
fonte
3
Olá, você poderia compartilhar conosco o que escolheu e como funcionou para você? Existem bibliotecas em Python que todos gostamos de usar, mantendo as opções de desempenho e sem bloqueio. Obrigado
Maziyar
Que tal gerar / forçar um processo e se comunicar por meio do sistema de E / S, como este sugere: sohamkamani.com/blog/2015/08/21/python-nodejs-comm ?
lkahtz
Existe uma nova biblioteca de pontes denominada PyNode que permite chamar o Python e obter os tipos JS retornados. É demonstrado aqui thecodinginterface.com/blog/…
SciGuyMcQ

Respostas:

86

Para comunicação entre o node.js e o servidor Python, eu usaria soquetes Unix se ambos os processos fossem executados no mesmo servidor e soquetes TCP / IP. Para o protocolo de empacotamento, eu levaria JSON ou buffer de protocolo . Se o Python encadeado parecer um gargalo, considere usar o Twisted Python , que fornece a mesma simultaneidade orientada a eventos que o node.js.

Se você se sentir aventureiro, aprenda clojure ( clojurescript , clojure-py ) e obterá a mesma linguagem que é executada e interopera com o código existente em Java, JavaScript (node.js incluído), CLR e Python. E você obtém um excelente protocolo de empacotamento usando simplesmente estruturas de dados clojure.

Aleš Kotnik
fonte
2
Você sabe se algo assim funcionará no Heroku, que possui um sistema de arquivos efêmero?
cm2
119

Isso soa como um cenário em que zeroMQ seria um bom ajuste. É uma estrutura de mensagens semelhante ao uso de soquetes TCP ou Unix, mas é muito mais robusta ( http://zguide.zeromq.org/py:all )

Existe uma biblioteca que usa zeroMQ para fornecer uma estrutura RPC que funciona muito bem. Chama-se zeroRPC ( http://www.zerorpc.io/ ). Aqui está o olá mundo.

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

E o cliente 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, servidor 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 o cliente 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
fonte
4
O zerorpc pode lidar com vários estados no evento em que há várias sessões do cliente?
user1027169
Boa resposta, exemplos de exemplos, explicações abundantes e o que eu estava procurando. TY. +1
Gaurav Gandhi
1
se você é novo como eu, instalar as dependências que maneira mencionados aqui - ianhinsdale.com/code/2013/12/08/...
Darpan
Muito obrigado por isso!
Gezim
1
Olá bom mundo demo! Outra solução semelhante abaixo usando Rabbitmq. medium.com/@HolmesLaurence/…
teng
7

Se você deseja ter seu trabalhador Python em um processo separado (processo do tipo servidor demorado ou um filho gerado sob demanda), sua comunicação com ele será assíncrona no lado do node.js. Os soquetes UNIX / TCP e a comunicação stdin / out / err são inerentemente assíncronos no nó.

lanzz
fonte
6

Eu consideraria também o Apache Thrift http://thrift.apache.org/

Pode fazer a ponte entre várias linguagens de programação, é altamente eficiente e oferece suporte para chamadas assíncronas ou sincronizadas. Veja todos os recursos aqui http://thrift.apache.org/docs/features/

A linguagem múltipla pode ser útil para planos futuros; por exemplo, se você quiser fazer parte da tarefa computacional em C ++ mais tarde, é muito fácil adicioná-la à mistura usando o Thrift.

Iftah
fonte
5

Eu tive muito sucesso usando thoonk.js junto com thoonk.py . A Thoonk utiliza o Redis (armazenamento de valores-chave na memória) para fornecer alimentação (pense em publicar / assinar), fila e padrões de tarefas para comunicação.

Por que isso é melhor do que soquetes unix ou soquetes tcp diretos? O desempenho geral pode diminuir um pouco, no entanto, a Thoonk fornece uma API realmente simples que simplifica a necessidade de lidar manualmente com um soquete. A Thoonk também ajuda a tornar realmente trivial a implementação de um modelo de computação distribuída que permita dimensionar seus trabalhadores python para aumentar o desempenho, pois você apenas cria novas instâncias de seus trabalhadores python e os conecta ao mesmo servidor redis.

Doug McCall
fonte
3

Eu recomendo usar alguma fila de trabalho usando, por exemplo, o excelente Gearman , que fornecerá uma ótima maneira de enviar tarefas em segundo plano e obter o resultado de forma assíncrona quando processados.

A vantagem disso, muito usada no Digg (entre muitos outros), é que ele fornece uma maneira forte, escalável e robusta de fazer com que trabalhadores em qualquer idioma falem com clientes em qualquer idioma.

Pierre
fonte
1

Atualização 2019

Existem várias maneiras de conseguir isso e aqui está a lista em ordem crescente de complexidade

  1. Python Shell, você escreverá fluxos no console python e ele escreverá de volta para você
  2. Redis Pub Sub, você pode ter um canal ouvindo em Python enquanto o nó js publisher envia dados
  3. Conexão Websocket em que o Node atua como cliente e o Python atua como servidor ou vice-versa
  4. Conexão de API com Express / Flask / Tornado, etc., trabalhando separadamente com um terminal de API exposto para o outro consultar

Abordagem 1 Python Shell Abordagem mais simples

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

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

Notas : Crie uma pasta chamada assinante que esteja no mesmo nível do arquivo source.js e coloque o destination.py dentro dela. Não se esqueça de mudar seu ambiente virtualenv

PirateApp
fonte