Node.js: libuv线程池的工作原理是什么?

5

我正在学习Node Js,我了解Node Js的核心是基于事件循环的反应器模式。

当任何事件发生时,它会进入事件队列,然后在运行任务结束后被堆栈拾取。如果事件是非阻塞的,则会这样处理,但如果它是一个阻塞请求,则事件循环将其传递给libuv线程池中的线程。

现在我的疑惑是:

  1. 执行完成后,libuv线程是否将请求传回事件队列或事件循环?不同的教程有不同的情况。

  2. libuv中的线程池有3个以上的线程,现在假设有10个用户同时尝试登录(像Facebook这样的应用程序),并且由于它们想要连接到数据库而阻塞了线程,那么只有三个线程如何处理这么多负载?

我真的很困惑,没有找到一个好的解释这些疑问的地方,任何帮助都将不胜感激。


默认情况下,Libuv使用4个线程,但可以使用UV_THREADPOOL_SIZE进行更改。 - SomeDutchGuy
2个回答

11

当任何事件发生时,它都会进入事件队列,然后被堆栈选中。

事件不会被堆栈直接选中,而是由事件循环传递给调用堆栈。

如果它是一个阻塞请求,那么事件循环将其传递给libuv线程池中的线程。

只有四件事使用线程池——DNS查找、fs、加密和zlib。其他所有东西都在主线程中执行,无论是否阻塞。

因此,日志记录是一种网络请求,线程池不能处理这个请求。无论libuv还是node,都没有任何代码来处理与网络请求相关的低级操作。相反,libuv委托请求发出到底层操作系统,然后等待操作系统发出信号,表明某些响应已经返回到请求。操作系统跟踪网络堆栈中的连接。但是,网络 I/O 是由您的网络硬件和 ISP 处理的。


只有一个备注。自定义的C++模块也可能使用线程池。 - SomeDutchGuy
@Yilmaz "它只是等待操作系统发出信号,表明某些响应已经返回到请求" 这是否意味着它进入了空闲状态或事件循环被阻塞?还是它注册了一些事件来持续轮询操作系统请求的状态? - sujeet
@8bitIcon libuv文件系统操作与套接字操作不同。套接字操作使用操作系统提供的非阻塞操作。文件系统操作在内部使用阻塞函数,但在线程池中调用这些函数,并在需要应用程序交互时通知已注册事件循环的观察者。 - Yilmaz
@8bitIcon https://nikhilm.github.io/uvbook/filesystem.html - Yilmaz
@8bitIcon http://docs.libuv.org/en/v1.x/design.html@8bitIcon http://docs.libuv.org/en/v1.x/design.html - Yilmaz

2
一旦执行结束,libuv线程会将请求传递回事件队列或事件循环吗?
从Node.js的角度来看,这会有所区别吗? libuv的线程池多了3个线程,假设现在有10个用户同时尝试登录(比如Facebook等应用),由于它们要连接到数据库,所以线程被阻塞了。那么如何让只有三个线程来处理这么大的负载呢? libuv使用线程池,但不是"天真"地使用。大多数异步请求是文件系统/ TCP 交互,通过 `select()` 处理。只有在创建自定义的C++模块并手动分派一个CPU/IO阻塞任务时,你才需要担心线程池。

一些加密函数也无法进行轮询。 - SomeDutchGuy

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