Javascript與Python之間的程序間通訊

banq發表於2024-06-15


關於如何實現 Javascript/Node.js <--> Python IPC 的快速小教程。

為了促進 Javascript/Node.js Web 伺服器和 Python AI 子程序之間的通訊,可以使用標準輸入 (stdin) 和標準輸出 (stdout) 流建立程序間通訊 (IPC)。

Javascript
雖然 Node.js 有一個稱為 IPC 的 fanceh 訊息傳遞系統,但在與不執行 Javascript/Node.js 的程序通訊時,它實際上不起作用。為此,解決方案是使用子程序的標準輸入 (stdin) 和標準輸出 (stdout) 進行通訊:

1、從 Node.js 生成 Python 子程序
讓我們從編寫父 Node.js 指令碼開始。首先,我們需要生成 Python 子程序,因此我們這樣做:

import { spawn } from 'child_process';
const python = spawn(<font>"path/to/child.py", {
    stdio: [
"pipe", "pipe", "inherit" ]
});

這將生成 Python 指令碼作為子程序,並將 stdin 和 stdout 設定為“管道”模式以便進行互動,並將 stderr 設定為“繼承”模式以共享父程序的錯誤流。

我們將 stdin 和 stdout 設定為pipe模式(這讓我們可以與流互動),並將標準錯誤 (stderr) 設定為inherit模式,這允許它共享父程序的 stderr。這樣,子程序中的錯誤就會向上傳播,並最終出現在父程序將其輸出傳送到的同一個日誌檔案中。

2、傳送資料到 Python 子程序
如果您需要向 Python 子程序傳送一些資料,則必須等到它初始化後才能傳送一些資料:

python.on(`spawn`, () => {
    console.log(`[node:data:out] Sending initial data packet`);
    python.stdin.write(`start\n`);
});

子程序初始化後,就可以透過python.stdin.write() 向其傳送資料。

在呼叫 child_process.spawn 時設定一個環境變數,即在上述選項物件中設定 env: { key: "value" }。

3、讀取 Python 子程序的響應
接下來,我們需要讀取 Python 指令碼的響應。讓我們開始吧:

import nexline from 'nexline'; <font>// Put this import at the top of the file<i>

const reader = nexline({
    input: python.stdout,
})

for await(const line of reader) {
    console.log(`[node:data:in] ${line}`)
}

nexline 軟體包用於透過 python.stdout 從 Python 子程序中高效地逐行讀取響應。

最簡單的方法是監聽 python.stdout 上的資料事件,但這並不能保證到達的每個資料塊實際上都是一行資料,因為程序間的資料不像在終端顯示內容時那樣採用行緩衝。

要解決這個問題,我建議使用我最喜歡的 npm 軟體包之一:nexline。不管你信不信,以最小的緩衝來高效處理這個問題比聽起來要困難得多,所以使用一個軟體包來幫你解決這個問題會更容易。

透過一個漂亮的 for await...of 迴圈,我們可以高效地讀取 Python 子程序的響應。

如果您真的要這樣做,我建議將其封裝在 EventEmitter(Node.js)/EventTarget(WHAT WG 瀏覽器規範,Node.js 也有)中。

Python 子程序
Python 指令碼從 sys.stdin 讀取輸入,並將響應寫入 sys.stdout。呼叫 sys.stdout.flush() 對於確保立即傳送響應至關重要。

import sys

sys.stderr.write(f<font>"[python] hai\n")
sys.stderr.flush()

count = 0
for line in sys.stdin:
    sys.stdout.write(f
"boop" + str(count) + "\n")
    sys.stdout.flush()
    count += 1

簡單!我們只需遍歷 sys.stdin,即可從父 Node.js 程序中讀取資料。

我們可以寫入 sys.stdout,將資料傳送回父程序,但重要的是要呼叫 sys.stdout.flush()!Node.js 沒有類似的命令,因為它很聰明,但在 Python 中,除非呼叫 .flush() 強制它傳送響應,否則它可能直到不知道什麼時候(如果有的話)才會傳送響應。

結論
這種方法可在 Javascript 和 Python 之間實現高效的程序間通訊,使應用程式能夠充分利用兩種語言的優勢。

我們在這裡處理的是純文字訊息,建議使用 JSON

  • JSON.stringify()
  • JSON.parse() (Javascript)


  • json.dumps() 
  • json.loads (Python)

 來序列化/反序列化訊息,以確保穩健性。

JSON 預設不包含換行符,並且會將任何存在的字元轉義為 \n,因此在這種情況下應該是安全的。

完整程式碼:
index.mjs:

#!/usr/bin/env node
<font>"use strict";

import { spawn } from 'child_process';
import nexline from 'nexline';

///<i>
// Spawn subprocess<i>
///<i>
const python = spawn(
"/tmp/x/child.py", {
    env: {  
// Erases the parent process' environment variables<i>
       
"TEST": "value"
    },
    stdio: [
"pipe", "pipe", "inherit" ]
});

python.on(`spawn`, () => {
    console.log(`[node:data:out] start`);
    python.stdin.write(`start\n`);
});

child.py:

#!/usr/bin/env python3
import sys

sys.stderr.write(f<font>"[python] hai\n")
sys.stderr.flush()

count = 0
for line in sys.stdin:
    # sys.stderr.write(f
"[python:data:in] {line}\n")
    # sys.stderr.flush()

    sys.stdout.write(f
"boop" + str(count) + "\n")
    sys.stdout.flush()
    count += 1

 

相關文章