Node與Python 雙向通信的實現(xiàn)代碼
第三方數(shù)據(jù)供應商把數(shù)據(jù)和Python封裝到一起,只能通過調用 Python方法來實現(xiàn)數(shù)據(jù)查詢,如果可以通過Node 簡單封裝下實現(xiàn) Python 方法調用可以快速上線并節(jié)省開發(fā)成本。
最簡單粗暴的通信方式是 Nodejs調用一下 Python 腳本,然后獲取子進程的輸出,但是由于每次 Python 啟動并加載數(shù)據(jù)包的過程比較漫長,所以對該過程優(yōu)化。
進程通信
index.py
# 封裝的 Python 包, 體積巨大
from mb import MB
# 從數(shù)據(jù)包中查詢
mbe.get('1.0.1.0')
index.js
const { spawn } = require('child_process');
const ls = spawn('python3', ['index.py']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code $[code]`);
});
通過child_process.spawn來派生 Python 子進程,監(jiān)聽 stdout 輸出。上述方式也是官方文檔中的示例,目前該示例存在兩個問題:
- Nodejs 沒有向 Python 發(fā)送數(shù)據(jù)
- Nodejs 調用完畢后,Python 子進程會退出;下次查詢需要再次調用Python命令進行加載文件,查詢數(shù)據(jù);無法實現(xiàn)一次內存加載,多次使用。
進程雙向通信
保證一次數(shù)據(jù)加載,多次使用的前提是 Python 進程啟動后不能退出。Python 進程之所以退出是因為無事可做,所以常見的手段有循環(huán),sleep,監(jiān)聽端口,這些手段可以翻譯成同步阻塞任務,同步非阻塞任務,其中代價最小的就是同步非阻塞任務,然后可以想到 Linux 的 select,epoll,簡單搜索了下 Python 的 epoll,好像還有原生的包。
index.py - 通過 epoll 監(jiān)聽 stdin
import sys
import fcntl
import select
from mb import MB
import json
mbe = MB('./data')
# epoll 模型
fd = sys.stdin.fileno()
epoll = select.epoll()
epoll.register(fd, select.EPOLLIN)
try:
while True:
events = epoll.poll(10) # 同步非阻塞
data = ''
for fileno, event in events:
data += sys.stdin.readline() # 通過標準輸入獲取數(shù)據(jù)
if data == '' or data == '\n':
continue
items = xxx # 數(shù)處理過程
for item in items:
result = mbe.get(item)
sys.stdout.write(json.dumps(result, ensure_ascii=False) +'\n') # 寫入到標準輸出
sys.stdout.flush() # 緩沖區(qū)刷新
finally:
epoll.unregister(fd)
epoll.close()
index.js - 通過 stdin 發(fā)送數(shù)據(jù)
const child_process = require('child_process');
const child = child_process.spawn('python3', ['./base.py']);
let callbacks = [],
chunks=Buffer.alloc(0),
chunkArr = [],
data = '',
onwork = false; // buffer 無法動態(tài)擴容
child.stdout.on('data', (chunk) => {
chunkArr.push(chunk)
if (onwork) return;
onwork = true;
while(chunkArr.length) {
chunks = Buffer.concat([chunks, chunkArr.pop()]);
const length = chunks.length;
let trunkAt = -1;
for(const [k, d] of chunks.entries()) {
if (d == '0x0a') { // 0a 結尾
data += chunks.slice(trunkAt+1, trunkAt=k);
const cb = callbacks.shift();
cb(null, data === 'null' ? null : data )
data = '';
}
}
if (trunkAt < length) {
chunks = chunks.slice(trunkAt+1)
}
}
onwork = false;
})
setInterval(() => {
if (callbacks.length) child.stdin.write(`\n`); // Nodejs端的標準輸入輸出沒有flush方法,只能 hack, 寫入后python無法及時獲取到最新
}, 500)
exports.getMsg = function getMsg(ip, cb) {
callbacks.push(cb)
child.stdin.write(`${ip}\n`); // 把數(shù)據(jù)寫入到子進程的標準輸入
}
Python 與 Nodejs 通過 stdio 實現(xiàn)通信; Python 通過 epoll 監(jiān)聽 stdin 實現(xiàn)駐留內存,長時間運行。
存在問題
- Nodejs 把標準輸出作為執(zhí)行結果,故 Python 端只能把執(zhí)行結果寫入標準輸出,不能有額外的打印信息
- Nodejs 端標準輸入沒有 flush 方法,所以 Python 端事件觸發(fā)不夠及時,目前通過在Nodejs端定時發(fā)送空信息來 hack 實現(xiàn)
- Buffer 沒法動態(tài)擴容,沒有C語言的指針好用,在解析 stdout 時寫丑
總結
雖然可以實現(xiàn) Nodejs 和 Python 的雙向通信,然后由于上述種種問題,在這里并不推薦使用這種方式,通過 HTTP 或 Socket 方式比這個香多了。
到此這篇關于Nodejs與Python 雙向通信的實現(xiàn)代碼的文章就介紹到這了,更多相關Nodejs與Python雙向通信內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用Nodejs編寫一個腳本實現(xiàn)markdown轉pdf功能
Markdown?是一種輕量級的標記語言,非常適合用來寫作和記錄,將?Markdown?轉換為?PDF?可以讓文檔在格式和樣式上更加統(tǒng)一,也方便在不同設備和平臺上查看和打印,在接下來的內容中我們將講解如何使用?NodeJs?編寫一個?Markdown?轉?PDF?的腳本來實現(xiàn)我們這個想要的功能2024-05-05
Node.js模擬發(fā)起http請求從異步轉同步的5種用法
這篇文章主要介紹了Node.js模擬發(fā)起http請求從異步轉同步的5種方法,下面總結了幾個常見的庫 API 從異步轉同步的幾種方法。需要的朋友可以參考下2018-09-09
nodejs版本過高導致vue-cli項目無法正常運行的幾種解決方案
這篇文章主要給大家介紹了關于nodejs版本過高導致vue-cli項目無法正常運行的幾種解決方案,在項目中你可能需要用到的node版本太低,但是你所下的node版本是最新的,這時候就會報錯,需要的朋友可以參考下2023-07-07
詳解nodejs實現(xiàn)本地上傳圖片并預覽功能(express4.0+)
本篇文章主要介紹了nodejs實現(xiàn)本地上傳圖片并預覽功能(express4.0+) ,具有一定的參考價值,有興趣的可以了解一下2017-06-06
Node.js在child_process域和錯誤冒泡及捕獲實踐
這篇文章主要為大家介紹了Node.js在child_process域和錯誤冒泡及捕獲實踐示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

