NodeJS批量多进程处理 - 子进程池中的子进程(或多线程)

10

NodeJS批量多线程处理 - 使用池中的子进程。

我知道子进程是一个进程,而不是一个线程。我使用了错误的语义,因为大多数人在讨论“多线程”时知道你的意图。因此,我将保留这个术语在标题中。


想象一下一个场景,您需要使用单个自定义函数或模块连续地完成多个类似且复杂的任务。在这种情况下,最好使用所有可用的CPU内核/线程(例如8/16),这就是child_process.fork()的作用。

理想情况下,您希望有多个同时运行的工作进程,并向/from一个控制器发送/回调消息。

node-cpoolfork-poolchild-pool等模块正是做到这点,但它们看起来已经过时/未维护/不受欢迎。

虽然有很多类似的模块,但它们似乎最相关。它们共同之处就是只有几个提交记录,很少被赞和fork,并且已经被弃用。

通常情况下,当我找不到一个看起来在各个方面都很有意义的任务时,是因为我错过了更好的方法。因此,我的问题是:

如何为我的自定义模块创建一个受管理、排队、多线程并行fork()池?

TAGGwebworker-threads这样的多线程模块并不相同,因为它们不支持完整的模块(带有二进制编译组件)。


PS

我目前正在使用 fork-pool 这个模块,它似乎完全符合我的要求,但有些小问题。我不相信这样一个鲜为人知、不太受欢迎的模块是唯一可行的选择。


2
child_process.fork() 创建的是一个进程而不是一个线程。为每个 CPU 密集型任务创建一个进程似乎并不正确。[tag:node.js](在没有第三方本地模块的帮助下)为 IO 密集型应用程序提供了单线程环境。您可以编写一个多语言应用程序,并使用 [tag:message-queue] 将 CPU 密集型任务卸载到多线程环境中。 - fardjad
1
抱歉,我在问题中没有正确处理语义。我知道fork()会创建一个进程。事实上,_进程__和__线程__都可以在硬件通道的一个"每个核心线程"_中独立执行代码。就这个问题而言,请假设我知道自己在做什么,并且运行8个进程是可以的。我从来没有听说过有人抱怨软件运行速度加倍。Node提供了一个多线程工具集,即child_process,专门为需要它的人提供服务。 - Redsandro
4个回答

1
我希望提供一个选项,虽然不能完全回答您的问题,但在选择技术方面有灵活性的情况下可能会对类似情况有用。如果将工作转移到.NET环境(C#,F#,IronPython,PowerShell等)是可接受的,您可能会对Edge.js项目感兴趣。这样,您可以使用Node进行IO密集型工作,并将计算密集型工作委托给.NET运行时在同一进程中托管。Edge.js提供了与.NET代码的高效互操作性,允许利用.NET任务并行库和其他功能,而无需产生生成额外进程的开销。
当你制作混合应用程序时,会涉及到维护和技术成本。请仔细评估你所获得的利益,并确保根据你项目的优先级来确定是否值得这些成本。
Node.js不适合执行阻塞、CPU密集型工作负载。Node.js的区别设计特点是其单线程、基于事件循环的架构。通常,Node.js应用程序通过将处理委托给外部进程或服务来处理CPU密集型工作负载。这涉及跨越进程边界并产生额外的延迟。(更多)
没有充分的理由就引入复杂性是愚蠢的。如果Node.js可以自行处理任务,则添加对.NET的依赖可能过度。然而,在许多情况下它可能是有价值的。成为一个好的工程师需要一些思考。

3
这有点过头了。建议用.NET替代使用某种类型的队列就有些荒谬,尤其是因为你仍然需要一个队列来传递消息到/从.NET。此外,他们可能正在运行OSX或Linux,这使得.NET成为更糟糕的建议。 - Jessie A. Morris
@Jessie:我强烈不同意一般性的观点,但同意在特定情况下可能会过度。这取决于许多因素,包括任务的计算强度有多大。有一大类问题可以从这种方法中受益。此外,为什么Linux/OSX会成为问题呢? - Roman Boiko
我确实同意这取决于其他因素。至于Linux/OSX支持,C#、F#、PowerShell等在Windows以外的平台上的支持并不是很好。Java、C、C++等可能更适合跨平台开发。 - Jessie A. Morris
我完全同意@JessieA.Morris的看法。如果你是一个.Net商店,想尝试一下NodeJS,那不是个坏主意,但如果你想做NodeJS,就不要引入这样一个巨大的依赖项。它会增加太多复杂性。 - tadman
2
“愚蠢”这个词用得太委婉了。就像引入Java(JVM)或Haskell一样荒谬,除非它们提供了NodeJS本身无法实现的功能。即使如此,如果Java或Haskell提供更好的工具支持,我仍然会主张使用它们来编写解决方案,而不是将两个平台融合在一起。 - tadman
1
这是一个有价值的选项,所以我已经点赞了这个答案。然而,在我的特定情况下,我对池化 进程 感兴趣,因为它们可以是任何东西。任何二进制组件(C++甚至汇编)都可以被封装成模块。如果它们是独立的二进制文件,则可以在 spawn() 中封装它们,而不是 fork(),尽管在我的具体情况下,我正在寻找一些带有编译二进制组件的节点模块进行多进程处理。(另外,我使用的是Linux系统) - Redsandro

1

2
听起来有趣,点赞。但是这看起来有点像“工作量太大且依赖项太多”。我只想池化一些进程。这不应该需要太多的代码和依赖项。我现在正在使用fork-pool,它似乎正好符合我的要求,但我不能相信这样一个未知和不受欢迎的模块会是唯一可行的选择。 - Redsandro

1

Web Workers 标准定义了一种让 JavaScript 使用多个线程并且可以并行处理比单线程更多的工作的方法。

在 NodeJS 中有几种实现方式,包括 webworker-threads NPM 模块。

使用 fork 是走向一个通常更难协调的多进程路径。NodeJS Cluster 系统试图缓解这里的很多摩擦,但还远非理想。


哦,我忘记了Web Workers。那是一个很好的解决方案。 - Jessie A. Morris
2
谢谢,但我在问题的最后一部分提到了这些。它们涵盖了仅适用于纯可评估(eval())JavaScript 的实现。具有二进制组件的模块(如大多数高效模块,例如解析器(xml-expat))永远无法以这种方式使用。JavaScript 线程太受限制了。在某些(这种)情况下需要 Fork()。它只会增加 30 毫秒的执行时间和每个进程 10MB 的内存使用量,开发人员可能有充分的理由选择它。 - Redsandro

1

我最近遇到了一个问题,即在单个node.js进程中创建的分支池化问题,并想出了自己的解决方案来解决该问题。我终于成功将解决方案导出到自己的npm模块中,您可以在此处查看:

https://www.npmjs.com/package/forkpool

你可以创建一个单一的池来管理所有的fork,或者创建多个池来管理隔离的工作批次。例如,我的一个应用程序有两个池——一个用于管理与图像处理相关的fork,另一个用于视频处理。由于视频处理比图像处理更加密集,因此在8核机器上,视频处理池的大小为2,而图像处理池的大小为4。
我希望随着时间的推移不断改进这个模块,所以请随时在Github存储库中提出问题或增强请求。

https://github.com/manthanhd/forkpool


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