关于Node.js内部异步I/O机制的困惑

25
  1. 我了解到在*nix平台上,node.js使用libeio内部执行异步文件 I/O,并且有线程池,我的理解是对的吗?
  2. 那么异步网络 I/O 呢? 是由libev完成的吗?这里也有一个线程池吗?
  3. 如果有线程池,它如何比传统的一线程请求模型更有效率?它是否是一个I/O请求一个线程?
  4. 在Windows上的机制是什么?我知道它是通过IOCP完成的,有一个内核级别的线程池,对吧?
  5. 为什么Linux还没有像Windows IOCP一样的原生完全AIO机制呢?将来会有吗?

根据changchang的答案更新:

  1. 我快速查看了@changchang提供的源代码,发现默认的线程池大小可以通过UV_THREADPOOL_SIZE重新设置,我想知道在哪些情况下会使用这个选项?
  2. 我还发现getaddrinfo也使用了这个线程池,除了fs之外还有其他用途吗?如果所有同步作业都在这个线程池中完成,那么默认大小的“4”是否足够?
  3. 据我现在的理解,node.js进程中将有6个基本线程:1个V8线程(事件循环,在用户JavaScript代码运行的地方),1个libuv事件循环和4个线程池,我的理解是对的吗?
  4. 我该如何在Ubuntu shell中看到这些线程?我只看到了两个:ps -eLf | grep node | grep -v grep
  5. root 16148 7492 16148 0 2 20:43 pts/26 00:00:00 ./bin/node /home/aaron/workspace/test.js
    根目录下,有一个名为"test.js"的文件,在终端中以"./bin/node /home/aaron/workspace/test.js"命令运行,进程ID为16148,父进程ID为7492。


请参见此处:https://dev59.com/rGgv5IYBdhLWcg3wVPTU - user568109
1
Node.js 实际上使用 libuv 来为所有支持的平台抽象异步 IO。 - Milan
@user568109,我已经阅读了这篇文章,但是没有得到明确的答案,事实上,不清晰的表述让我更加困惑。它提到libeio“异步执行输入输出”,包括套接字,我对此表示怀疑。我从某个地方学到的是:由于无法在常规文件上使用epoll,因此出现了libeio来使用线程执行aio。 - Aaron Wang
@simfoo 是的,我知道libuv,我想知道它背后的机制,包括*nix和Windows。 - Aaron Wang
2个回答

31
  1. 首先,libuv已经将libeio移除。但是它像你提到的一样,使用一个线程池来执行异步文件I/O。

  2. libuv还删除了libev。它基于不同平台的异步I/O接口(如epollkqueueIOCP)执行异步网络I/O,而不需要线程池。有一个事件循环在uv的主线程上运行,它轮询I/O事件并处理它们。

  3. libuv内部的线程池是一个固定大小的线程池(在类Unix系统中为4)。它扮演任务队列的角色,并通过避免在请求数量增加时无限生成线程来避免系统资源的枯竭。


非常感谢,很多资源都已经过时了,你刚刚救了我! - Aaron Wang
2
@AaronWang 如果这个回答解决了你的问题,你应该接受它。 - travis
1
首先:libuv[...]可以在没有线程池的情况下进行异步网络I/O操作。 之后:libuv内部的线程池[...]。 这对我来说似乎有些矛盾。 - Rafael Eyng
4 不是一个非常小的数字吗,尤其是对于 io 绑定任务而言。Node 允许更改池大小吗? - Mangat Rai Modi
是的,通过 UV_THREADPOOL_SIZE 它可以实现。 - Youssef Mohamed

2
直到版本0.6,Node使用libev运行事件循环和libeio进行异步I/O(Unix后端严重依赖这两个库)。但是从版本0.8开始,libuv开始取代libevlibeio。它执行、维护和管理事件池中的所有I/O和事件。libuv 是跨平台异步IO库的首选。
  1. 是的,直到节点0.6版本,从0.8开始弃用并使用线程池。
  2. 是的,但是libev不使用线程池。请参见这里

    澄清:根据我发布的问题中的链接libeio支持处理I/O(包括套接字)的所有POSIX函数。但是节点作者决定仅将其用于异步文件I/O,并使用libev进行网络I/O。我不知道您从哪里听说的,但您可以在常规文件上使用epoll。

  3. libev使用事件循环,因此没有问题。

  4. 是的,IOCP在Windows中处理异步I/O,内核确实使用线程池。
  5. 新的Linux内核具有epoll,在新的BSD内核中具有kqueue。 libevlibeio适用于Linux环境,并为所有内核提供事件循环/异步I/O(支持select、poll、epoll、kqueue)。

更新问题:

  1. 不太了解 libuv
  2. 可能足够了(不确定)
  3. 这是我对 Windows 8 的研究结果,通过 Process Explorer 检查。显示一个 node 应用程序进程的 4 个线程、1 个 DLL、1 个文件和 1 个区段(总共 7 个条目)。

  4. ps -eLf 会显示所有线程和进程,也许你正在过滤它,只需查找像 ps -eLf | grep x 中的 node 进程 pid,其中 x 是 node 进程的 pid。


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