如果我们可以更改线程池使用的线程数,为什么还需要节点集群?

问题描述

标题基本上说明了一切,当我们可以更改 libuv 线程池使用的线程数时,为什么还要创建节点工作线程?

解决方法

libuv 线程池不用于您的 JavaScript 代码,仅用于 Node.js API 的一个子集(尽管它被最重要的 API 之一 fs 使用)。来自the documentation

Node.js 会尽可能使用异步系统 API,但在它们不存在的情况下,libuv 的线程池用于创建基于同步系统 API 的异步节点 API。使用线程池的 Node.js API 有:

  • 所有 fs API,文件观察器 API 和显式同步的 API 除外
  • 异步加密 API,例如 crypto.pbkdf2()crypto.scrypt()crypto.randomBytes()crypto.randomFill()crypto.generateKeyPair()
  • dns.lookup()
  • 所有 zlib API,显式同步的 API 除外

因此,libuv 线程池的大小有助于处理大量重叠的 fs 和类似调用,但这并不是全部。

如果您的 JavaScript 代码需要同步完成大量工作,那么 libuv 池对您没有帮助;该代码在单个线程上运行(除非您启动工作程序)。此外,Node.js 使用同一个线程来检查异步工作的完成(包括 libuv 完成)。来自event loop page

下图显示了事件循环操作顺序的简化概述。

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle,prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections,│
│  └─────────────┬─────────────┘      │   data,etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

这样,即使没有运行 JavaScript 代码,一个线程也会做很多工作。

如果您启动工作线程,它们每个都有自己的事件循环,并且可以处理与其工作相关的完成,即使另一个线程正忙于执行占用大量 CPU 的工作。

因此,工作线程在任何给定情况下是否有用在很大程度上取决于您的代码在做什么。