Node.js 在跨多个磁盘驱动器的文件系统 I/O 中的性能:工作线程与否?

问题描述

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

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

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

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

解决方法

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

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

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

如果没有工作线程,您将有一个单独的 libuv 线程池来处理所有磁盘 I/O 请求。因此,它们都将进入同一个池中,一旦该池中的线程忙碌(无论它们正在服务的磁盘是什么),新请求将按照它们到达的顺序排队。这可能不太理想,因为如果您对驱动器 A 有 5 个请求,对驱动器 B 有 1 个请求,对驱动器 C 有 1 个请求,您不希望首先用驱动器 A 的 5 个请求填满池,因为这将使驱动器 B 和驱动器 C 的请求要等到驱动器 A 上的几个请求完成后才能启动。这会失去一些跨独立驱动器的并行性的机会。当然,能否真正在单独的驱动器上实现并行还取决于驱动器控制器的实现以及它们是否真的具有单独的 SATA 控制器。

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

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

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

,

只是为了扩展每个进程模型,它看起来与 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 要求所需的数量很少,则为每个内核运行一个工作程序。随着您添加驱动器或更多服务器,工作人员可以轻松扩展。