NodeJS Event Loop Fundamendals

时间:2015-05-31 04:09:37

标签: node.js javascript-events event-loop

我确信这是一个常见问题,但没有找到具体的答案。

我理解NodeJS的基本概念,以及处理I / O的异步/非阻塞性质。

为了论证,让我们举一个简单的例子,说明在节点中编写的HTTP服务器执行unix命令'find /'并将结果写入http响应(因此在用户的浏览器上显示命令的结果)。 我们假设这需要3秒钟。

我们假设有两个用户'A'和'B'在同一时间通过他们的浏览器请求。

据我所知,用户的请求在事件队列中排队(消息A,消息B)。该消息还引用了它在处理完成后要执行的相关回调。

因为,事件循环是单线程的,并逐个处理事件,

在上面的示例中,“用户B”的回调是否需要6秒才会被触发? [3表示“用户A”的事件处理,3表示自己的事件处理]

这听起来像我在这里遗漏了一些东西?

最糟糕的是,如果100个用户在同一毫秒内请求?第100个事件所有者将成为最不幸的用户,并且必须等待永恒。

据我所知,运行时只有一个事件队列,上述问题可适用于应用程序任何部分的任何用户。例如,网页X中的慢速数据库查询会减慢网页Y中的不同用户的速度?

从根本上说,我发现事件的串行处理和相关回调的串行执行存在问题。

我在这里错过了什么吗?

1 个答案:

答案 0 :(得分:2)

正确编写的node.js服务器将对任何网络,磁盘I / O,计时器或与其他进程的通信使用异步I / O和通信。以这种方式编写时,可以并行处理多个http请求。虽然处理任何给定请求的node.js代码一次只运行一个,但只要一个请求等待I / O(通常是请求的大部分时间),就可以运行其他请求。

最终结果是所有请求似乎同时进行(尽管实际上,它们之间的工作是交织在一起的)。 Javascript事件队列是在所有各种请求之间序列化工作的机制。每当异步操作完成它的工作或希望通知某个事件的主JS线程时,它就会在事件队列中放入一些东西。当JS执行的当前线程完成时(即使它有自己的异步操作正在进行中),JS引擎查看事件队列然后执行该队列中的下一个项目(通常是某种形式的回调),并且方式,下一个排队的操作继续进行。

在您的特定示例中,当您启动另一个进程然后异步等待其结果时,当前执行的线程结束,然后事件队列中的下一个项目才会运行。如果下一个项目是另一个http请求,则该请求开始处理。当第二个请求,然后命中一些异步点时,它的执行线程结束,并再次运行事件队列中的下一个项目。通过这种方式,新的http请求开始,并且已经完成的异步操作的异步回调开始运行。事情大致以FIFO(先进先出)的顺序发生,它们是如何放入事件队列的。我说“粗略”,因为实际上有不同类型的事件,并非所有事件都是相同的序列化,但为了讨论的目的,可以忽略实现细节。

因此,如果三个http请求在完全相同的时间到达,那么一个将运行直到它到达异步点。然后,下一个将运行,直到它到达异步点。然后,第三个将运行,直到它遇到异步点。然后,无论哪个请求完成其第一个异步操作,都将从该异步操作获得回调,它将一直运行直到完成或命中另一个异步点。等等......

由于通常会导致Web服务器花费很多时间来响应的大部分内容通常是某种I / O操作(磁盘或网络),这些操作都可以在node.js中异步编程,这整个过程通常都能很好地工作实际上,服务器资源的效率要高于每个请求使用一个单独的线程。有一次它不能很好地工作,如果有一个繁重的计算密集型或一些长时间运行,但不是异步操作,长时间绑定主node.js线程。因为node.js系统是一个协作的CPU共享系统,如果你有一个长时间运行的操作,它绑定了主node.js线程,它将占用系统(没有先发制人的共享与其他操作一样可以使用多线程系统)。占用系统会使所有其他请求等到第一个请求完成。 node.js对一些CPU占用计算的回答是将一个操作移动到另一个进程并与node.js线程中的其他进程异步通信 - 从而保留单个node.js线程的异步模型。

对于node.js数据库操作,数据库通常会为node.js编程提供异步接口,以便以异步方式使用数据库,然后由数据库接口的实现来实际实现接口。异步时尚。这可能是通过与实现实际数据库逻辑的其他一些进程(可能通过TCP进行通信)进行通信来完成的。实际的数据库逻辑可能使用或不使用实际线程 - 这是一个由数据库本身决定的实现细节。对node.js来说重要的是计算和数据库工作在某个其他进程中的node.js线程之外,甚至可能在另一个主机上,因此它不会阻塞node.js线程。