node项目中调用执行python脚本的方法详解
一、环境配置
Windows + PowerShell 命令:
- 在node项目中新建python文件夹,以及子文件夹scripts用于放置python脚本
- 创建虚拟环境
cd python->python -m venv venv - 进入虚拟环境并install脚本中的依赖:
venv\Scripts\Activate.ps1-> 单个安装pip install pandas - 不要把 venv 提交到 git
// .gitignore python/venv/ __pycache__/ *.pyc
只提交依赖清单
执行:pip freeze > requirements.txt 提交这个文件即可
服务器部署时重新创建环境
python -m venv venvpip install -r requirements.txt
二、运行python脚本
方法一:用spawn 子进程通信(不建议)
缺点:频繁 spawn 会很慢,stdout 容易污染
注意:
- 绝对路径的拼接,以及脚本里如果有读取文件代码时的文件路径
- Python脚本里不能随便 print,因为node必须自己保证输出是 JSON
const { spawn } = require("child_process");
const path = require("path");
const rootPath = path.resolve(__dirname, "..");
// 1️⃣ 定位 Python 解释器路径:E:\node\xxx\python\venv\Scripts\python.exe
const pythonPath = path.join(
rootPath,
"python",
"venv",
"Scripts",
"python.exe",
);
// 2️⃣ 定位 Python 脚本路径:E:\node\xxx\python\scripts\test.py
const scriptPath = path.join(
rootPath,
"python",
"scripts",
"test.py",
);
// 🔐 全局执行锁
let isRunning = false;
// 注意脚本文件里访问文件时的路径,要用绝对路径
exports.get_info = (req, res) => {
if (isRunning) {
return res.send({
status: 429,
message: "任务正在执行中,请稍后再试",
});
}
isRunning = true;
// 第二个参数是数组,表示执行哪个脚本,和传给 Python 的参数
const py = spawn(pythonPath, [scriptPath]);
let error = "";
py.stderr.on("data", (data) => {
error += data.toString();
});
py.on("error", (err) => {
console.error("启动失败:", err);
isRunning = false;
return res.send({
status: 500,
message: "Python 启动失败",
error: err.message,
});
});
// 超时杀进程
const timeout = setTimeout(() => {
py.kill("SIGTERM");
}, 300000); // 300秒超时
py.on("close", (code) => {
clearTimeout(timeout);
isRunning = false;
if (code === 0) {
return res.send({
status: 200,
message: "Python脚本执行成功",
});
}
console.error("Python执行错误:", error);
return res.send({
status: 500,
message: "Python执行失败",
error: error || "未知错误",
});
});
};
每个请求都 spawn 一个 Python 进程,如果接口被并发打(比如前端连点多次请求、或者有压测),就会同时起多个 python.exe 导致内存瞬间爆掉,所以这里限制了单进程:
①在脚本test.py里强制限制线程,放在所有import之前
import os os.environ["OPENBLAS_NUM_THREADS"] = "1" os.environ["OMP_NUM_THREADS"] = "1" os.environ["MKL_NUM_THREADS"] = "1"
②加单进程锁(不允许并发执行),以及加超时杀进程(防卡死)
同一时间只执行一个 Python 脚本,后面的请求直接拒绝,在“单进程 + spawn 模式”下,只要 Python 进程正常退出,内存会被系统回收。
方法二:使用FastAPI(推荐)
用 Python 起一个 API 服务,Node 通过 HTTP 调用,稳定性最高,避免:spawn 死锁、buffer 溢出、JSON污染
Step1:进入虚拟环境安装 FastAPI 和 uvicorn
cd python venv\Scripts\Activate.ps1 pip install fastapi "uvicorn[standard]"
Step2:Python 起 API 服务,新建入口文件 api_server.py
# api_server.py
import os
from fastapi import FastAPI
import sys
# 关键:让 python 能识别 scripts 目录
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(BASE_DIR)
# 从 scripts 引入
from scripts.test import get_test_info
app = FastAPI()
# 注册路由:
@app.get("/aaa/bbb")
async def ccc():
result = await get_test_info()
return result
Step3:把脚本文件改成接口驱动模式的函数写法
比如异步任务时 if __name__ == "__main__": 改成async 函数 async def get_test_info(): 并return json:
return {
"status": 200,
"file": result_file
}
注意:FastAPI 本身已经运行在事件循环里,不能在脚本文件里 asyncio.run()
Step4:在不同端口启动这个 FastAPI 应用实例:uvicorn api_server:app --host 0.0.0.0 --port 8000 --reload
Step5:不再 spawn,改成 axios 请求:
const axios = require("axios");
// 全局执行锁
let isRunning = false;
// 注意脚本文件里访问文件时的路径,要用绝对路径
exports.get_info = async (req, res) => {
if (isRunning) {
return res.send({
status: 429,
message: "任务正在执行中,请稍后再试",
});
}
isRunning = true;
try {
const response = await axios.get(
"http://127.0.0.1:8000/aaa/bbb",
{
timeout: 300000, // 5分钟超时
}
);
isRunning = false;
return res.send({
status: 200,
message: "Python脚本执行成功",
data: response.data,
});
} catch (error) {
isRunning = false;
return res.send({
status: 500,
message: "FastAPI调用失败",
error: error.message,
});
}
};
到此这篇关于node项目中调用执行python脚本的方法详解的文章就介绍到这了,更多相关node调用python脚本内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Nodejs进阶:基于express+multer的文件上传实例
本篇文章主要介绍了基于express+multer的文件上传实例,现在分享给大家,也给大家做个参考,感兴趣的小伙伴们可以参考一下。2016-11-11


最新评论