如何使用 node.js 在 stdout 中编写阻塞?
我正在编写一个 node.js 应用程序,其中 stdout 通过管道传输到文件。我正在用 console.log 编写所有内容。一段时间后,我的应用程序达到 1GB 限制并停止。有趣的是,如果我使用 console.error 而不是 console.log,内存使用率保持较低并且程序运行良好。所以看起来 Node.js 无法刷新标准输出流,所有内容都保存在内存中。我想让 stderr 不出现错误。
我的问题是:
有没有办法将阻塞写入标准输出?或者至少,我可以使用对标准输出的回调进行写入,这样我就可以确保我不会写太多?
谢谢!
I'm writing a node.js application which stdout is piped to a file. I'm writing everything with console.log. After a while my Application reaches the 1GB Limit and stops. The interesting thing is, that if I use console.error instead of console.log, the memory usage keeps low and the programm runs fine. So it looks like node.js can't flush the stdout stream and everything is kept in memory. I wanna keep stderr free for errors.
My Question is:
Is there a way to write blocking into stdout? Or at least, can I write with a callback to stdout, so I can ensure I'm writing not too much?
thx!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果你真的想要同步写入标准输出,你可以这样做:
If you really really want synchronous writes to stdout you can do:
使用
process.stdout.write
编写,返回值是数据是否被缓冲。如果为 true,则在process.stdout
发出drain
事件时继续写入。如果您希望代码看起来同步,请按照此处所述使用streamlinejs: Node.js stdoutlush
Write using
process.stdout.write
, the return value is whether data got buffered. If it's true, continue writing whenprocess.stdout
emits thedrain
event.If you want your code to look sync, use streamlinejs as described here: Node.js stdout flush
出现该问题(内存使用量爆炸)的原因可能是您的程序创建输出的速度比显示输出的速度快。因此你想限制它。您的问题要求“同步输出”,但实际上可以通过使用纯粹的“异步”(*) 代码来解决问题。
(*注意:在这篇文章中,术语“异步”用于“javascript-单线程”的含义。这与传统的“多线程”含义不同,后者完全不同)
这个答案展示了如何将“异步”代码与 Promise 结合使用,通过“暂停”(而不是阻塞)执行直到成功刷新写入输出来防止内存使用量激增。该答案还解释了异步代码解决方案与同步代码解决方案相比有何优势。
问:“暂停”听起来像“阻塞”,异步代码怎么可能“阻塞”?这是一个矛盾的说法!
答:它之所以有效,是因为 javascript v8 引擎仅暂停(阻止)执行等待异步承诺完成的单个代码片,同时允许其他代码片执行同时。
这是一个异步写入函数(改编自 此处)。
可以从源代码中的异步函数调用它,例如
情况 1
其中
main()
是从顶层调用的唯一函数。内存使用量不会像调用console.log('hello world');那样激增。需要更多上下文才能清楚地看到相对于真正的同步写入的优势:
情况 2
运行上面的代码(情况 2)会显示内存使用量仍然没有爆炸(与情况 1 相同),因为 < code>logger 关联的切片已暂停,但 CPU 使用率仍然如此,因为
essentialWorker
切片没有暂停 - 这很好(想想新冠肺炎)。相比之下,同步解决方案也会阻塞
essentialWorker
。多个切片调用
streamWrite
会发生什么?情况3
本情况(情况3),内存使用受限,
essentialWorker
CPU使用率较高,同<案例 2。hello world
和goodbye world
的各个行将保持原子性,但这些行不会干净地交替,例如,可能会出现。
The problem (exploding memory usage) probably occurs because your program is creating output faster than it can be display. Therefore you want to throttle it. Your question requests "synchronous output", but actually the problem can be solved by using purely "asynchronous"(*) code.
(* NOTE: In this post the term "asynchronous" is used in the "javascript-single-thread" sense. That differs from the conventional "multi-thread" sense which an entirely different kettle of fish).
This answer shows how "asynchronous" code can be used with Promises to prevent memory usage from exploding by "pausing" (as opposed to blocking) execution until the write output has been successfully flushed. This answer also explains how an asynchronous code solution can be advantageous compared to a synchronous code solution.
Q: "Paused" sounds like "blocked", and how can asynchronous code possibly "block"? That's an oxymoron!
A: It works because the the javascript v8 engine pauses (blocks) execution of only the single code slice await an asynchronous promise to complete, while permitting other code slices to execute in the meantime.
Here is an asynchronous write function (adapted from here).
It can be called from an asynchrous function in your source code, e.g.
case 1
Where
main()
is the only function called from the top level. The memory usage will not explode as it would if callingconsole.log('hello world');
.More context is required to clearly see the advantage over a true synchronous write:
case 2
Running the above code (case 2) would show that the memory use is still not exploding (same as case 1) because the
logger
associated slice was paused, but the CPU usage was still because as theessentialWorker
slice was not paused - which is good (think COVID).In comparison, a synchronous solution would also block the
essentialWorker
.What happens with multiple slices calling
streamWrite
?case 3
In this case (case 3), the memory usage is bound, and the
essentialWorker
CPU usage is high, the same as in case 2. Individual lines ofhello world
andgoodbye world
would remain atomic, but the lines would not alternate cleanly, e.g.,could appear.
编辑:正如评论者指出的,这个解决方案有问题。 printResolver 可以做成一个数组,但使用顶级解决方案要容易得多。
同步打印函数也可以使用异步/等待,与管道(又名 FIFO)一起使用。
确保您始终使用“等待打印”来调用“打印”
Edit: as indicated by commenter, this solution has a problem. printResolver could be made into an array, but using top solution is much easier
A synchronous print function that also works with pipes aka FIFO's, using Async/await.
Make sure you always call "print" with "await print"
不。
您想要做的是当输出已满时
pause()
您的输入,就像pump()
方法一样,然后resume()
当你有空间写的时候就可以了。如果没有,您的流程就会变得非常庞大。不过,您可能想使用更直接的
outputStream
内容,或者write()
调用,而不是console.log()
。Don't.
What you want to do is
pause()
your input when the output is full, like thepump()
method does, thenresume()
it when you've got space to write. If not, your process balloons out to gargantuan size.You probably want to use the more direct
outputStream
stuff for that, though, or thewrite()
call, notconsole.log()
.