nodejs事件循环(EventLoop)中的poll阶段是怎么实现的?

126 阅读4分钟

nodejs的特点,我们首先会想到,非阻塞异步io。

第一:怎样实现非阻塞的呢?

通过我们所熟知的“事件循环(event loop)”。

第二:怎么实现异步io的呢?

  • 第一:假如不采用多路复用,只采用异步io,那么nodejs进程与系统 (调用一次只能检查一个文件描述符)
  • 第二:采用io多路复用(其实不管是java也好,nodejs也好。都是采用的io多路复用),(通过一次系统调用,检查多个文件描述符的状态) ,这样可以最大程度的节省性能的消耗。

备注文件描述符状态可以理解为io操作的3种状态。(io操作完成已经就绪,io操作未完成,io操作出错)

结论:很明显io多路复用性能优于单个的异步io,可以减少进程或线程与操作系统内核间的调用,通过一次操作系统调用就能监听多个文件描述符。

io多路复用是基于什么做到的呢?

先解释下操作系统中io多路复用的三种机制,

  • 1: select

特点1: 每次需要全量拷贝文件描述符到操作系统内核。(关键词:“全量拷贝”,是不是比较消耗性能

特点2: 内核获取到要监听的文件描述符后就需要不断轮训,这个文件描述符列表,获取可用的文件描述符。(关键词:“轮训”,是不是比较消耗性能

特点3: 每个进程内使用的文件描述符数量是有限制的。(这个在编译操作系统内核是就写死了所有进程能用的对打文件数了)。(关键词:“数量是有限制的”,每个进程能监听的io操作是不是被限制了

  • 2: poll

与select类似

  • 3: epoll

特点1: 采用红黑树的方式保存文件操作符。(关键词:“红黑树”,每次要新监听一个文件描述符,只需要new一个新的描述符,插入树中。而且没有大小限制

特点2: 采用事件机制的方式通知文件描述符就绪,不需要轮训整棵红黑树的文件描述符。(关键词: 事件机制 ” 通过事件机制就能获取到哪个文件描素符就绪,不用轮训整棵树

特点3: 采用链表的方式保存所有已经准备就绪的文件描述符。

通过上面的select,poll,epoll对比,epoll的性能明显好于select和poll。nodejs正是采用epoll的方式实现io多路复用

poll阶段的timeout是怎么来的?

因为select,poll,epoll在监听文件描述符时,会采用阻塞的方式去获取当前是否有准备就绪的文件描述符,如果没有就会一直在这轮训。类似while(1) { 轮训已经就绪的文件描述符 }。若没有准备就绪的就会继续轮训。这样肯定是不合理的,所以引入了timeout。

我们nodejs中poll节点的timeout就是这样来的。

timeout设置为null会怎么样?

如果timeout设置为null,那么当前进程没有io操作时,就会一直阻塞在poll阶段(也就是一直阻塞在epoll一直轮训可用文件描述符阶段)

nodejs的poll阶段,基于下面那种io复用机制实现的?

1: select

2: poll

3: epoll

上面已经给出答案了:采用 epoll,应为epoll性能更好。

补充:epoll方式中,采用时间内机制通知文件描述符是否准备就绪。那什么时候通知呢?

1: 水平触发

  • 只要是文件描述符对应的文件准备就绪后,
  • 进程会来读取准备就绪的文件,
  • 若是当前没有读取完文件内容,之后还会触发文件描述符准备就绪事件。

这样是不是会触发多次文件描素符准备就绪的事件。

2: 边缘触发

  • 让准备就绪的文件能一次就读取完成,
  • 不用再次触发文件描述符准备就绪事件。

减少事件的触发,提升新能