Nodejs - 线程池大小为 4 时可以 运行 的最大线程是多少?

Nodejs - What is maximum thread that can run same time as thread pool size is four?

要求每秒并发请求1000个,每个请求进行数据库查询等IO操作。由于 nodejs 工作在事件循环上,它会将 IO 操作分配给线程池,但线程池默认大小为 4,因此同时最多 4 个线程(IO 操作)可以工作,其余必须在队列中等待。一旦任何线程完成执行,它们就可以处理。

查询 1 - 我们能否根据要求将线程池大小增加到 N 个,这会提高性能还是会降低性能?

问题 2 - 我们如何在 nodejs 中实现上述要求?

查询 3 - Nodejs 是此需求或其他建议(例如 golang)的闲置选择

libuv 中的网络套接字是非阻塞的(即不在那个线程池上)。首先构建测试工具。您很可能对默认设置没问题。

要增加线程池大小,请将环境变量 UV_THREADPOOL_SIZE=N 设置为 1024。

$ node --help
...
Environment variables:
...
UV_THREADPOOL_SIZE            sets the number of threads used in libuv's threadpool

nodejs 中的网络不使用线程池,因此切换不会影响您的网络 I/O 吞吐量。网络使用已经异步的 OS API 和 non-blocking.

您在处理传入请求时运行的 Javascript 也不使用线程池。

Disk I/O 确实使用线程池,但是如果您只访问一个物理磁盘驱动器,那么增大线程池可能不会给您带来多大好处,因为只有一个物理磁盘伺服器只能无论如何一次在一个地方所以 运行 20 个并行的磁盘请求如果它们都在争夺同一个磁盘头定位,则不一定对您有帮助。事实上,它甚至可能让事情变得更糟,因为 OS 试图在所有不同的线程之间进行时间片,导致磁盘磁头移动超过为每个线程提供服务的最佳状态。

要每秒处理 1000 个请求,您必须进行基准测试并找出瓶颈所在。如果我不得不猜测,我敢打赌瓶颈将是您的数据库,在这种情况下,重新配置 nodejs 设置不是您需要集中精力的地方。但是,无论如何,只有确定特定应用程序中的瓶颈所在后,您才能正确地找出哪些选项可以帮助您解决该瓶颈。此外,请记住,服务 1000 requests/sec 意味着您不能 运行 Javascript 每个请求花费超过 1 毫秒的每个请求。因此,您可能还必须对服务器进行集群(通常是服务器硬件中每个物理 CPU 核心一个集群)。所以,如果你有一个 8 核服务器,你将设置一个 8 节点集群。

例如,如果您 CPU 使用自己的 运行 限制了您的 nodejs 进程 Javascript,那么也许您想实施 nodejs 集群以获得多个 CPU 全部 运行 个不同的请求。但是,如果真正的瓶颈在您的数据库中,那么集群您的 nodejs 请求处理程序将无助于解决数据库瓶颈。

基准、衡量、根据数据提出理论以改变什么,设计具体的测试来衡量,然后实施其中一种理论和衡量。根据您测量的内容进行调整。你只能通过先测量,做出适当的调整然后测量进度来真正正确地做到这一点(不要在无用的方向上浪费大量时间)。

node.js 上的网络 I/O 操作在主线程上运行。

是的,node.js 除主线程外还生成四个线程,但其中 none 用于网络 I/O,例如数据库操作。线程是:

  1. DNS 解析器(因为大多数 OS 为此只提供同步 API)

  2. 文件系统API(因为异步做起来很麻烦cross-platform)

  3. 加密(因为这使用了CPU)

  4. Zlib(zip压缩)

除非您自己生成 worker_threads,否则其他一切都不要使用线程。有关此的更多信息,请参阅节点自己的文档:https://nodejs.org/en/docs/guides/dont-block-the-event-loop/。不要依赖非来自 node.js 项目本身的信息,例如 youtube 或说节点 I/O 使用线程池的媒体文章 - 他们不知道他们在说什么。

增加线程池大小对网络 I/O 没有任何作用,因为 node.js 根本没有任何代码让网络 I/O 利用额外的线程。如果你想将负载分散到多个处理器上,你可以使用集群。您可以编写自己的集群代码或使用进程管理器的集群模式,例如 pm2 将连接传递给您的进程。

node只用ONE THREAD怎么能号称高性能呢!

大多数 non-systems 程序员没有意识到的是,等待 I/O 只需要零 CPU 时间。通过产生线程来做到这一点意味着你正在分配大量的 RAM,并且大部分时间都使用 zero CPU 时间用于所有这些线程(想象一下产生 1024 个线程,每个线程都不使用 CPU)。当这些线程(或者在 node.js 主线程的情况下)正在等待来自数据库的 1000 个回复时,OS 将这些请求排队成一系列数据包并将它们发送到您的网卡依次将它们发送到数据库 一次一位 - 是的,I/O 在它的核心不是并行的(除非你在多个网卡上使用中继)。因此,大部分繁重的工作都是由以太网完成的,而您的进程被 OS(等待)暂停。

node.js 的作用是在请求等待时发出另一个请求。这就是 non-blocking 的意思。节点在处理所有其他请求之前不会等待请求完成。这意味着默认情况下,您在 node.js 中发出的所有请求都是并发的 - 它们不会等待其他请求完成。

在请求完成端,从服务器收到的任何响应都会触发节点搜索事件队列(此时实际上它只是一个集合,因为队列中的任何项目都可以随时完成)并找到相应的回调称呼。执行回调 do 需要 CPU 时间但不等待网络请求。

这就是为什么像 node.js 这样的系统可以与 multi-threaded 系统竞争的原因。事实上,在某些情况下可以胜过 multi-threaded 系统,因为在同一个线程上执行它意味着你不需要锁(互斥锁或信号量)并且你避免了上下文切换的成本(当 OS 放置一个线程休眠,将所有寄存器值复制到 RAM,然后唤醒另一个线程,从 RAM 为新进程复制寄存器值。