Node.js子进程 - spawn和fork的区别

193

这可能看起来像一个基础问题,但我找不到任何文档:

forking 和 spawning 一个 node.js 进程有什么区别?我已经读到 forking 是 spawning 的一种特殊情况,但它们各自使用的不同用例 / 影响是什么?

4个回答

270

Spawn是一个命令,旨在运行系统命令。当你运行spawn时,你发送的是一个将在自己的进程中运行的系统命令,但不会在节点过程中执行任何其他代码。你可以为所生成的进程添加监听器,以允许你的代码与生成的进程进行交互,但没有创建新的V8实例(除非你的命令是另一个Node命令,在这种情况下,你应该使用fork!),并且处理器上只有一个副本你的节点模块。

Fork是spawn的特殊实例,它运行全新的V8引擎实例。这意味着,你可以创建多个工作线程,运行在完全相同的Node代码库上,或者为特定任务创建不同的模块。这对于创建工作线程池非常有用。虽然节点的异步事件模型允许单个机器核心相当高效地使用,但它无法利用多核机器的节点进程。最简单的方法是在单个处理器上运行多个相同程序的副本。

一个好的经验规则是每个核心一到两个节点进程,也许对于具有良好ram时钟/cpu时钟比率的机器或针对I/O重CPU轻的节点进程来说,每个核心可以用更多的进程,以最小化事件循环等待新事件的停机时间。然而,后一项建议是微优化,需要仔细地进行基准测试,以确保你的情况适合每个核心的多个进程的需求。过多地生成工作线程可能会降低性能。

最终,你可以使用spawn以实现上述目标,只需发送一个Node命令即可。但这是愚蠢的,因为fork对优化创建V8实例的过程做了一些工作。只是要明确,最终spawn包括fork。fork只是针对这种特定的,非常有用的用例而优化的。

http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback


@ChrisCM,如果我在我的主应用程序中使用var child = require('child_process').fork('child.js');,那么现在我将有两个独立的核心正在运行。如果我在child.js(进程)中运行一个重型循环,那么实际上我将利用更多的核心来支持child.js,对吗?但是,这种CPU使用会影响我的主应用程序核心吗? - NiCk Newman
2
在 CPU 上做任何事情都不可能不影响其他事情。调度、共享缓存使用、总线流量等等。然而,它应该利用一个单独的核心,并且使您的主运行循环基本上不受影响。也就是说,在同一个单核处理器上运行两个进程所带来的严重负面影响并不会发生。此时,真正取决于操作系统和硬件设置的适当优化。不同的设置可能会产生不同的结果。 - MobA11y
@ChrisCM 是的,我使用全局MonsterLoop来同步怪物位置,它迭代的对象可以多达5,000个键。我每2秒迭代一次它,并且分叉似乎将数百内存使用量从我的CPU(主游戏)中削减掉了。我宁愿这样做,而不是将该循环聚类并使其在每个核心上运行xx次...感谢您的见解~现在我只是不知道是否应该使用Redis还是内部IPC :P - NiCk Newman
2
感谢您提及“为什么” - 在我阅读的所有帖子中,直到这篇文章都错过了解释的这个简单部分。 - aaaaaa
@ChrisCM 在你的回答中提到了“..但不会在您的节点进程中执行任何其他代码..”。这是否意味着主线程正在等待并且没有处理任何内容..如果是,那么在这里使用spawn有什么用处..? - Abhi
生成的进程不会执行。执行生成操作的进程将继续异步运行,如常。 - MobA11y

39

Spawn

当调用 Spawn 时,它会在父进程和子进程之间创建一个 流接口(streaming interface)

流接口 — 一次性以二进制格式缓冲数据。

Fork

当调用 Fork 时,它会在父进程和子进程之间创建一个 通信通道(communication channel)

通信通道 — 消息传递。

Spawn 和 Fork 的区别

虽然两者在数据传输方面听起来很相似,但它们存在一些区别。

  • 当你想要在二进制/编码格式下进行持续的数据传输时,Spawn 很有用—例如传输 1GB 的视频、图像或日志文件。
  • 当您想要发送单个消息时,Fork 是很有用的—例如 JSON 或 XML 数据消息。

结论

应该使用 Spawn 将大量数据流式传输,例如从生成的进程向父进程传输图像。

应该使用 Fork 发送 JSON 或 XML 消息。例如,假设从父进程创建了十个分叉进程。每个进程执行一些操作。对于每个进程,完成操作将向父进程发送一个消息,例如“第4个进程完成了”或“第8个进程完成了”。


父进程不断记录数据,然后传递给子进程,最终写入文件,这个怎么处理? - Esqarrouth
3
@Esqarrouth,你需要确定是连续的流还是消息。你使用了"continuous loging"这个词,我相信你将写入日志(JSON)到子进程中,如果是这样,请使用FORK;否则,如果你有非常大的数据块要缓冲,则请使用SPAWN - vijay

9
  • spawnchild_process.spawn使用给定命令启动新进程。
  • forkchild_process.fork方法是创建子进程的特殊情况,是 spawn() 方法的一种。

spawn() 方法

child_process.spawn 方法使用给定命令启动新进程。它具有以下签名 -

child_process.spawn(command[, args][, options])

阅读有关选项的更多信息。

spawn() 方法返回流(stdout 和 stderr),应在进程返回大量数据时使用。当进程开始执行时,spawn() 开始接收响应。

fork() 方法

child_process.fork 方法是使用 spawn() 创建 Node 进程的特殊情况。它具有以下签名 −

 child_process.fork(modulePath[, args][, options])

fork 方法返回一个对象,该对象除了具有所有正常 ChildProcess 实例的方法外,还带有内置的通信通道。

1

Spawn -

  • 创建一个流接口。适用于二进制/编码格式的连续数据传输。
  • 不会创建新的v8实例。

Fork-

  • 创建父子进程之间的通信渠道。适用于以json/xml格式发送单个消息。

  • 创建一个新的v8实例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接