如何正确处理生成的 child_process 的 stdio?

Posted

技术标签:

【中文标题】如何正确处理生成的 child_process 的 stdio?【英文标题】:How to properly handle the stdio of a spawned child_process? 【发布时间】:2021-12-31 10:12:15 【问题描述】:

我正在处理子进程。我正在制作一款游戏(几乎处于完成的最后阶段),现在我发现了一个严重的问题。

基本上,在游戏中,我有一个基于 javascript 的服务器,它使用 child_process 来spawn 一个运行 python 脚本的子进程。 python 脚本处理游戏机制,js 代码处理服务器-客户端系统。我需要一个 js 和我的 python 代码之间的通信系统。使用文档和其他资源,我已经成功设置了这样一个系统,但一段时间后,它停止工作。

这里是复制问题的代码:-

Javascript (temp.js):

"use strict";

const childProcess = require('child_process');

let pythonProcess = childProcess.spawn("python", ["temp2.py"], cwd: "./private/PythonScripts/Temp/");

pythonProcess.stdin.setDefaultEncoding("utf-8");
pythonProcess.stdout.setEncoding("utf-8");

const writeString = "" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
  "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\n";  // 8x  128 Zeros

pythonProcess.stdout.on("error", (err) => 
  console.log("Stdout Error");
  if (err) 
    console.log(err);
  
);

let count = 0;

setInterval(() => 
  count++;
  console.log(count);
  pythonProcess.stdin.write(writeString, (err) => 
    if (err) 
      console.log("Error during writing");
      console.log(err);
    
  );
, 100);

Python (3.10.x) (temp2.​​py):

import logging

logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    handlers=[logging.FileHandler('temp.log', 'w+', 'utf-8'), logging.StreamHandler()],
                    level=logging.INFO)

tempLogger = logging.getLogger(__name__)

if __name__ == '__main__':
    tempLogger.info("Started")
    while True:
        inp = input()
        tempLogger.info(inp)

目录结构:

temp.js
private
 |
 |-> PythonScripts
      |
      |-> Temp
           |
           |-> temp2.py
           |-> temp.log

当我们运行这段代码时,我们会看到 js 成功地继续写入(我假设是因为err 总是undefined),但是 python 只会记录输入只有 79 次。

我的预感是这个问题与缓冲区/管道溢出有关,并且它们没有被耗尽,但是在搜索所有文档以查找 child_process 并为 python 记录/输入之后,我无法弄清楚问题出在哪里,即正在阻止脚本之间的通信。

我该怎么做才能使数据流不会像现在一样卡住。

【问题讨论】:

【参考方案1】:

所以,我终于想通了。

问题是,我设置记录器的方式,记录器将写入日志文件以及控制台。

但由于进程是使用 child_process 创建的,我们无法看到。在我的主代码中,我为pythonProcess.stdout.on("data", (data) => ...); 设置了回调,但我们仍然看不到这些日志。当我独立运行脚本时,我注意到控制台的额外打印。最后,我可以通过为pythonProcess.stderr.on("data", (data) => ...); 设置处理程序来查看日志。

有两个修复:-

只需停止记录器登录控制台即可。 Python 代码将是: - (不要使用basicConfig()

import logging.handlers

tempLogger = logging.getLogger(__name__)
tempLogger.setLevel(logging.DEBUG)
handler = logging.handlers.WatchedFileHandler('temp.log', 'w+', 'utf-8')
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S'))
tempLogger.addHandler(handler)

if __name__ == '__main__':
    tempLogger.info("Started")
    with open("temp.txt", "r") as in_file:
        while True:
            for line in in_file.readlines():
                if line != "":
                    line = line.rstrip("\r\n")
                    tempLogger.debug(line)

像我上面提到的pythonProcess.stderr.on("data", (data) => ...); 那样为子进程的stderr 设置一个处理程序。这将清除 stderr 缓冲区,并且日志记录现在会卡住。

【讨论】:

以上是关于如何正确处理生成的 child_process 的 stdio?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Node.JS 中获取生成的 child_process 的输出?

nodejs child_process.spawnSync 或 child_process.spawn 包裹在 yieldable 生成器中,它返回输出

node.js 安全/转义中的 child_process 生成

使用命名管道作为标准输入生成节点 child_process

当节点进程被杀死时杀死所有child_process

如何使用 Bluebird 承诺 Node 的 child_process.exec 和 child_process.execFile 函数?