当前位置: 首页 > 后端技术 > Node.js

【翻译】nodejs事件循环部分1.1

时间:2023-04-03 19:33:51 Node.js

原文开头1.1概述:Reactor模式Reactor模式中的协调机制EventLoopReactor模式中的事件分离器EventDemultiplexerEventDemultiplexer无法处理的一些复杂的I/O接口,比如文件I/O、DNS等复杂I/O解决方案待续前言nodejs与其他编程平台的区别在于如何处理I/O接口。我们听到有人介绍nodejs的时候,总是说它是基于v8引擎,没有Blocking,事件驱动的语言,这些是什么意思呢?什么是“无阻塞”和“事件驱动”?所有的答案都在nodejs的核心——EventLoop中。在本系列文章中,我们将介绍事件循环是什么、它如何工作、它如何影响我们的应用程序、如何充分利用它等等。为什么不只是一个帖子而不是一个系列,因为那样它会是一个很长很长的帖子,我肯定会错过很多,所以我把它写成一个系列,在第一篇文章中,我将描述nodejs是如何工作的,如何传递I/O,如何与其他平台协同工作等。ReactorPatternnodejs工作在事件驱动模型中,涉及到事件分隔符和事件循环,所有的I/O请求最终都会产生一个事件完成的结果,事件失败,或其他事件的唤醒。这些事件会按照以下规则进行处理:1.事件分离器接收到I/O请求,然后将这些请求委托给相应的硬件2.已经处理过的请求(比如来自可读文件的数据,来自的数据接口可以读取),事件分离器会为要执行的特殊操作添加一个注册的回调程序。3.如果事件循环中可以处理事件,则按顺序执行,直到循环为空4.如果事件循环中没有事件,或者事件分隔符没有添加任何请求,则程序会完成,否则,程序将从第一步开始执行循环操作。这个整个项目的协调机制叫做EventLoopEventLoop其实就是一个单线程的半无限循环。为什么叫半无限呢?因为程序在没有工作要做的时候就退出了。从开发人员的角度来看,这些是程序退出的点。注意:不要混淆事件循环和事件发射器。EventEmitter是与该机制完全不同的概念。在上一篇文章中,我将解释EventEmitter如何通过EventLoop影响事件处理。上图是对NodeJs工作原理的高级概述,以及一种显示主要组件的设计模式,称为Reactor模式。但真正的复杂性远不止于此,到底有多复杂呢?事件多路分解器不是解析所有操作系统平台中所有I/O类型的单个组件。这里的Eventqueue不是一个单一的queue,里面所有类型的event都会入队或出队,I/O也不是唯一入队的eventtype。下面继续深挖EventDemultiplexerEventDemultiplexer并不是一个真正的组件,而是reactor模式中的一个抽象概念。现实中EventDemultiplexer在不同系统中的实现名称不同,如linux中的epoll、MacOS中的kqueue、Solaris中的eventpost、window系统中的IOCP等。nodeJS可以使用底层非阻塞、异步的硬件I/O事件多路分解器提供的功能。文件I/O的复杂性但令人恼火的是,并非所有类型的I/O都可以使用EventDemultiplexer执行,即使在同一个操作系统中,支持不同类型的I/O也非常复杂。一般来说,epoll、kqueue、eventports、IOCP都可以以非阻塞的方式进行网络I/O。但是文件I/O要复杂得多。某些系统,例如Linux,不支持以完全异步的方式访问文件系统。在MacOS系统中,文件系统对事件的发送和接收有限制(你可以在这里了解更多)。解决文件系统的所有复杂性以提供完全异步是非常复杂且几乎不可能的。DNS中的复杂性与文件I/O一样,节点API提供的一些DNS函数也具有一定的复杂性。例如,dns.lookup等NodeDNS函数会访问系统的一些配置文件,如nsswitch.conf、resolv.conf和/etc/hosts。上述文件系统的复杂性也适用于dns.resolve函数。解决方案?因此,引入了线程池来支持硬件异步I/O实用程序(如epoll/kqueue/eventports或IOCP)无法直接寻址的I/O功能。现在我们知道并不是所有的I/O函数都可以在线程池中运行。NodeJS已经尽量使用非阻塞和硬件异步I/O的方式来完成大部分的I/O功能,但是对于一些复杂和阻塞的I/O,还是通过引入线程池来解决,待续文章先翻译到这里,有翻译不好的地方请指出,我过几天继续发第二篇。