Объединение node.js и Python

128

Node.js идеально подходит для нашего веб-проекта, но есть несколько вычислительных задач, для которых мы бы предпочли Python. У нас также уже есть для них код Python. Нас очень беспокоит скорость. Каков самый элегантный способ вызвать Python "worker" из node.js асинхронным неблокирующим способом?

Cartesius00
источник
3
Привет, не могли бы вы поделиться с нами, что вы выбрали и как это сработало для вас? В Python есть библиотеки, которые мы все любим использовать, сохраняя при этом производительность и неблокирующие параметры. Спасибо
Maziyar 01
А как насчет простого создания / разветвления процесса и взаимодействия через системный ввод-вывод, как предлагает этот: sohamkamani.com/blog/2015/08/21/python-nodejs-comm ?
lkahtz
Существует новая библиотека мостов с именем PyNode, которая позволяет вызывать Python и получать возвращаемые типы JS. Это продемонстрировано здесь thecodinginterface.com/blog/…
SciGuyMcQ

Ответы:

86

Для связи между node.js и сервером Python я бы использовал сокеты Unix, если оба процесса выполняются на одном сервере, и сокеты TCP / IP в противном случае. Для протокола маршалинга я бы взял JSON или буфер протокола . Если поточный Python оказывается узким местом, рассмотрите возможность использования Twisted Python , который обеспечивает такой же управляемый событиями параллелизм, как и node.js.

Если вы любите приключения, изучите clojure ( clojurescript , clojure-py ), и вы получите тот же язык, который работает и взаимодействует с существующим кодом на Java, JavaScript (включая node.js), CLR и Python. И вы получаете превосходный протокол маршаллинга, просто используя структуры данных clojure.

Алеш Котник
источник
2
Вы знаете, будет ли что-то подобное работать на Heroku, имеющей временную файловую систему?
cm2
120

Это звучит как сценарий, в котором 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 __name__ == "__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)
djheru
источник
4
Может ли zerorpc обрабатывать несколько состояний в случае наличия нескольких клиентских сеансов?
user1027169
Хороший ответ, примеры примеров, подробные объяснения и то, что я искал. TY. +1
Гаурав Ганди
1
если вы новичок , как я, установить зависимости они , как упомянутые здесь - ianhinsdale.com/code/2013/12/08/...
Дарпан
Большое спасибо за это!
Gezim
1
Привет, мир демо! Другое аналогичное решение ниже с использованием Rabbitmq. medium.com/@HolmesLaurence/…
teng
7

Если вы разместите свой рабочий Python в отдельном процессе (либо длительном процессе серверного типа, либо порожденном дочернем процессе по запросу), ваше взаимодействие с ним будет асинхронным на стороне node.js. Сокеты UNIX / TCP и связь stdin / out / err по своей сути асинхронны в узле.

lanzz
источник
6

Я бы рассмотрел также Apache Thrift http://thrift.apache.org/

Он может связывать несколько языков программирования, очень эффективен и поддерживает вызовы async или sync. См. Полные возможности здесь http://thrift.apache.org/docs/features/

Многоязычный язык может быть полезен для будущих планов, например, если вы позже захотите выполнить часть вычислительной задачи на C ++, очень легко добавить его в смесь с помощью Thrift.

Ифты
источник
5

У меня был большой успех, используя thoonk.js вместе с thoonk.py . Thoonk использует Redis (хранилище ключей и значений в памяти), чтобы предоставить вам канал (например, публикацию / подписку), очереди и шаблоны заданий для общения.

Почему это лучше, чем сокеты unix или прямые сокеты tcp? Общая производительность может немного снизиться, однако Thoonk предоставляет действительно простой API, который упрощает работу с сокетом вручную. Thoonk также помогает упростить реализацию модели распределенных вычислений, которая позволяет масштабировать ваших работников python для повышения производительности, поскольку вы просто запускаете новые экземпляры своих работников python и подключаете их к тому же серверу Redis.

Дуг МакКолл
источник
3

Я бы рекомендовал использовать некоторую рабочую очередь, используя, например, отличный Gearman , который предоставит вам отличный способ отправлять фоновые задания и асинхронно получать их результат после обработки.

Преимущество этого, широко используемого на Digg (среди многих других), заключается в том, что он обеспечивает надежный, масштабируемый и надежный способ заставить сотрудников на любом языке говорить с клиентами на любом языке.

пьер
источник
1

Обновление 2019

Есть несколько способов добиться этого, и вот список в порядке возрастания сложности.

  1. Python Shell, вы будете писать потоки в консоль python, и она вам ответит
  2. Redis Pub Sub, вы можете прослушивать канал на Python, пока ваш издатель node js отправляет данные
  3. Соединение через WebSocket, где Node выступает в роли клиента, а Python - в качестве сервера или наоборот.
  4. Соединение API с Express / Flask / Tornado и т. Д., Работающее отдельно с конечной точкой 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()

Примечания : Создайте папку с именем subscriber, которая находится на том же уровне, что и файл source.js, и поместите в нее файл destination.py. Не забудьте изменить среду virtualenv

PirateApp
источник