Node.js python子脚本在完成时输出,而不是实时输出

Posted

技术标签:

【中文标题】Node.js python子脚本在完成时输出,而不是实时输出【英文标题】:Node.js's python child script outputting on finish, not real time 【发布时间】:2014-10-25 19:09:43 【问题描述】:

我是 node.js 和 socket.io 的新手,我正在尝试编写一个小型服务器,它将根据 python 输出更新网页。

最终这将用于温度传感器,所以现在我有一个虚拟脚本,每隔几秒打印一次温度值:

Thermostat.py

import random, time
for x in range(10):
    print(str(random.randint(23,28))+" C")
    time.sleep(random.uniform(0.4,5))

这是服务器的精简版:

Index.js

var sys   = require('sys'), 
    spawn = require('child_process').spawn, 
    thermostat = spawn('python', ["thermostat.py"]),
    app = require('express')(),
    http = require('http').Server(app),
    io = require('socket.io')(http);

thermostat.stdout.on('data', function (output)  
    var temp = String(output);
    console.log(temp);
    io.sockets.emit('temp-update',  data: temp);
); 

app.get('/', function(req, res)
    res.sendFile(__dirname + '/index.html');
    );

最后是网页:

Index.html

<!doctype html>
<html>
    <head>
        <title>Live temperature</title>
        <link rel="stylesheet" type="text/css" href="styles.css">
    </head>
    <body>
    <div id="liveTemp">Loading...</div>

    <script src="http://code.jquery.com/jquery-1.11.1.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io();
        socket.on('temp-update', function (msg) 
        $('#liveTemp').html(msg.data)
    );
    </script>

    </body>
</html>

问题是 nodejs 似乎一次接收所有温度值,而不是随机间隔获取 10 个温度值,我在脚本之后得到 all 一个长字符串中的值完成:

【问题讨论】:

【参考方案1】:

您需要在 python 中禁用输出缓冲。这可以通过多种不同的方式完成,包括:

设置PYTHONUNBUFFERED 环境变量 将-u 开关传递给python 可执行文件 每次写入标准输出后调用sys.stdout.flush()(或print())到标准输出 对于 Python 3.3+,您可以将 flush=true 传递给 print()print('Hello World!', flush=True)

此外,在您的节点代码中,(即使您在 python 代码中休眠并且您现在正在刷新标准输出)您真的不应该假设 output 在您的 thermostat.stdout 的“数据”处理程序中始终存在只是一行。

【讨论】:

sys.stdout.flush() 做到了! 这是一个很好的解决方案,但并不是真正的答案。为什么我需要禁用输出缓冲?节点不应该像命令行接收它一样接收缓冲的标准输出吗?禁用缓冲意味着如果您需要限制输出,那么您基本上必须自己重写缓冲逻辑,当 python 具有内置功能时,这似乎很愚蠢。 @Almenon 节点这边没有问题。节点进程无法接收python进程尚未写入的内容。 那为什么命令行可以在不指定-u开关的情况下实时接收输出呢? @Almenon 因为当您直接从终端运行 Python 脚本时,stdout 指向的是 tty 而不是管道。当 stdout 是管道时,输出是完全缓冲的。当 stdout 是 tty 时,输出是行缓冲的。您可以通过在节点spawn() 选项中设置stdio: [0, 1, 2] 来生成python 进程来验证这一点。这告诉节点将父进程的 stdin、stdout 和 stderr fds 传递给生成的进程,而不是设置管道。使用此设置,您将在 python 进程打印后立即看到每条打印的行。

以上是关于Node.js python子脚本在完成时输出,而不是实时输出的主要内容,如果未能解决你的问题,请参考以下文章

获取 node.js 中所有嵌套子进程的标准输出

Node.js 生成子进程并实时获取终端输出

Python 中的代码,在 Node.js 和 Socket.IO 中通信,在 HTML 中呈现

在文件中写入打印日志时,Python子进程在屏幕缓冲区中延迟

如何从 node.js 调用外部脚本/程序

如何在 node.js 子进程模块中将消息和标准输出从子进程传递给父进程?