1

我在这里阅读了几个关于 Node.js 以非阻塞方式快速处理文件 I/O 操作的能力与使用带有阻塞或非阻塞请求的工作线程的性能优势的问题答案,但似乎没有回答我的问题。

我正在编写一个 Node.js 应用程序,它将打开、散列和写入存储在多个硬盘驱动器上的非常大的文件(多个 gig)。我正在探索工作线程的想法,因为它们允许我将命令隔离到特定的硬盘驱动器。例如:假设我有一个线程处理将硬盘驱动器 A 上的一个文件复制到硬盘驱动器 B,另一个线程处理将一个文件从硬盘驱动器 C 复制到硬盘驱动器 D。

假设我同时将它扩展到更多的硬盘驱动器,对我来说只使用没有工作线程的 Node.js 并让它处理所有这些请求是否更有意义,或者如果我可以隔离工作线程是否更有意义按驱动器 I/O,并同时处理多个驱动器的请求?

鉴于我读过的内容,工作线程似乎是显而易见的解决方案,但我也看到让单个 Node.js 进程处理文件 I/O 队列通常更快。感谢您提供的任何指导!

4

2 回答 2

1

编辑:显然(基于下面的评论),nodejs 只有一个线程池在所有工作线程之间共享。如果是这种情况,那么为每个磁盘获得一个单独的池的唯一方法是使用多个进程,而不是多个线程。

或者,您可以扩大工作池,然后创建自己的队列系统,一次只将每个单独磁盘的几个请求放入工作池,从而在不同驱动器之间提供更多并行性。

原始答案(其中一些仍然适用):

如果没有工作线程,您将有一个单独的 libuv 线程池来服务所有磁盘 I/O 请求。因此,它们都将进入同一个池,一旦该池中的线程忙碌(无论它们正在服务什么磁盘),新请求将按照它们到达的顺序排队。这可能不太理想,因为如果您对驱动器 A 有 5 个请求,对驱动器 B 有 1 个请求,对驱动器 C 有 1 个请求,那么您不希望首先用 5 个对驱动器 A 的请求来填充池,因为这会使对驱动器 B 和驱动器 C 的请求等到驱动器 A 上的多个请求完成后才能开始。这失去了一些跨单独驱动器的并行性的机会。当然,

如果您确实使用了工作线程,每个磁盘一个nodejs工作线程,您至少可以保证每个磁盘的线程池中有一个单独的操作系统线程池,并且您可以更有可能没有一组请求一个驱动器将阻止对其他驱动器的请求有机会启动,并错过与对其他驱动器的请求并行运行的机会。

当然,现在所有这些讨论都是理论上的。在磁盘驱动器、控制器卡、控制器之上的操作系统以及 libuv 之上和 nodejs 之上的世界中,有很多理论讨论的机会在现实世界的测量中无法得到证实。

因此,真正确定的唯一方法是实现工作线程选项,然后将其与具有几种不同磁盘使用场景的非工作线程选项进行基准比较,包括您认为可能是最坏情况的几个。因此,与任何重要的与性能相关的问题一样,您将不可避免地需要进行基准测试和测量以肯定地知道一种或另一种方式。而且,您的结果也需要非常仔细地构建基准测试,以便最大限度地发挥作用。

于 2021-01-14T08:43:33.330 回答
0

只是为了扩展每个进程模型,它看起来与 jfriend00 建议的工作线程相同,只是 IPC 机制发生了变化。

从“头”管理器进程管理作业队列,并将驱动器/散列工作拆分为每个工作人员的节点进程,因此每个工作人员中没有 IO/CPU 争用。

让每个工作人员一次从其驱动器(或驱动器组)的工作队列中选择一个作业。您所需要的只是可靠性、监控未处理的工作人员以及工作人员完成/错误的返回渠道。

排队机制并不重要,一个数据库表就足够了(或任何类型的网络消息 http、grpc、nanomsg、redis、nats、rabbitmq)。这是基于不需要在经理和工人之间共享大量数据,除了简单的工作消息“嘿工人,这是一个文件,去处理它”,“我完成了这个文件”或“我坏了,帮助!”

| Manager                        |
| Pub 1 2 3                      |
| Sub 1    | Sub 2    | Sub 3    |
| Worker   | Worker   | Worker   |
|          |          |          |
| DriveA   | DriveC   | DriveE   |
| DriveB   | DriveD   | DriveF   |

如果进程最终受 CPU 限制,或者您需要管理 IO 要求,则为每个内核运行一个工作线程。当您添加驱动器或更多服务器时,工作人员可以轻松扩展。

于 2021-02-04T02:05:05.850 回答