การรวม node.js และ Python


128

Node.js เหมาะอย่างยิ่งกับโครงการเว็บของเรา แต่มีงานคำนวณเพียงไม่กี่งานที่เราต้องการ Python เรามีรหัส Python สำหรับพวกเขาด้วย เรากังวลอย่างมากเกี่ยวกับความเร็ววิธีใดเป็นวิธีที่ดีที่สุดในการเรียก Python "worker" จาก node.js ด้วยวิธีที่ไม่ปิดกั้นแบบอะซิงโครนัส


3
สวัสดีคุณช่วยบอกเราได้ไหมว่าคุณเลือกอะไรและได้ผลสำหรับคุณอย่างไร มีไลบรารีใน Python ที่เราทุกคนชอบใช้ในขณะที่ยังคงรักษาประสิทธิภาพและตัวเลือกที่ไม่ปิดกั้น ขอบคุณ
Maziyar

สิ่งที่เกี่ยวกับการวางไข่ / แยกกระบวนการและสื่อสารผ่าน IO ของระบบเช่นนี้แนะนำ: sohamkamani.com/blog/2015/08/21/python-nodejs-comm ?
lkahtz

มี bridging library ใหม่ชื่อ PyNode ที่ให้คุณเรียก Python และส่งคืน JS types แสดงให้เห็นที่นี่thecodinginterface.com/blog/…
SciGuyMcQ

คำตอบ:


86

สำหรับการสื่อสารระหว่าง node.js และเซิร์ฟเวอร์ Python ฉันจะใช้ซ็อกเก็ต Unix หากกระบวนการทั้งสองทำงานบนเซิร์ฟเวอร์เดียวกันและซ็อกเก็ต TCP / IP เป็นอย่างอื่น สำหรับ marshaling โปรโตคอลฉันจะใช้เวลา 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)

4
zerorpc สามารถจัดการหลายสถานะในเหตุการณ์ที่มีลูกค้าหลายเซสชันได้หรือไม่?
user1027169

คำตอบที่ดีตัวอย่างคำอธิบายมากมายและสิ่งที่ฉันกำลังมองหา TY +1
Gaurav Gandhi

1
ถ้าคุณยังใหม่เหมือนฉันติดตั้งการอ้างอิงตามที่กล่าวไว้ที่นี่ - ianhinsdale.com/code/2013/12/08/…
Darpan

ขอบคุณมากสำหรับสิ่งนี้!
Gezim

1
สวัสดีชาวโลกสาธิต! อีกวิธีที่คล้ายกันด้านล่างโดยใช้ Rabbitmq medium.com/@HolmesLaurence/…
teng

7

หากคุณจัดให้มีผู้ปฏิบัติงาน Python ของคุณในกระบวนการแยกต่างหาก (ไม่ว่าจะเป็นกระบวนการประเภทเซิร์ฟเวอร์ที่ใช้งานมานานหรือลูกที่เกิดตามความต้องการ) การสื่อสารของคุณกับคนนั้นจะเป็นแบบอะซิงโครนัสที่ด้าน node.js ซ็อกเก็ต UNIX / TCP และการสื่อสาร stdin / out / err โดยเนื้อแท้แล้ว async ในโหนด


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 ในขณะที่ผู้เผยแพร่โหนด 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 ไว้ข้างใน อย่าลืมเปลี่ยนสภาพแวดล้อมที่ดีของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.