我对事件和回调,同步/异步,调用堆栈和队列没有任何问题。
但是,据我所知,其他服务器为每个连接创建一个新线程,其中包含该请求响应的阻塞请求和处理程序,而在该节点中,此处理程序将作为回调传递给主线程。因此,这种服务器处理多个请求的能力受限于它在多个线程之间创建和切换的能力。
当Node接收到阻塞请求时,它会将其发送到异步域,同时继续处理主线程。在异步的情况下会发生什么,是否仍然需要创建一个线程来等待该请求的响应然后将事件发送到节点事件循环?如果是这样,为什么Node不受服务器在线程之间创建和切换的能力的限制?如果没有,请求会发生什么?
答案 0 :(得分:1)
我认为对事件循环的实际工作方式存在一些困惑。 NodeJS不会“收到阻止请求”并“将其发送到异步域”。它是异步的 - 除非你调用...Sync()
模式函数,每个调用和每个操作都是异步的。令人困惑的是,一旦你进入你的CODE,每个操作都是同步的。
这是一种“合作多任务处理”方法 - 所有对系统的调用都应该“开始滚动”并立即返回,而您自己的代码假设尽可能快地执行它需要做的事情并控制回来到JSVM(从你的函数返回)。
要了解在处理网络通信时这是如何工作的,您需要及时回到线程确实存在之前。在早期,如果你有多个网络连接,你的单线程进程必须将所需的所有套接字的列表放在一起(例如“有数据到达我是否可以读取?”),并询问通过调用select()
来实现操作系统。对于每个问题,每个套接字都是yes / no。这通常在while()
循环中完成,该循环一直运行直到程序终止。你会要求一个包含新数据的套接字列表,读取数据,用它做一些事情,然后一遍又一遍地重新入睡。
NodeJS要复杂得多,但这个比喻效果很好。它有一个主要的“事件循环”,一直在睡觉,直到有工作要做,然后醒来并做它。
您所做的一切都来自或进入此频道。如果您将数据写入网络套接字,并要求在完成后收到通知(回调),NodeJS会将您的请求传递给操作系统,然后进入休眠状态。你停止了跑步。保存您的上下文 - 保存所有本地变量。当操作系统回来并说“完成!”时,NodeJS检查其列表并看到您想知道这一点,并调用您的函数,重新加载您的上下文,以便您所需的所有本地变量。
非常清楚,完全可能的是,当数据完成写入网络,并且操作系统通知回来时,NodeJS正忙于其他工作! NodeJS不会“创建一个线程”来处理它 - 它会完全忽略它,直到它获得一些空闲时间!它不会丢失......它只是不会被“处理”。
这会驱动程序员用来线程化模型 - 看起来不合逻辑的是,这种永远不会立即响应传入事件的状态“直到它有机会”可能是有效的。但软件架构往往是骗人的。线程模型实际上具有相当高的开销。 CPU核心数量不是无限的 - 整个计算机整体上都在做大量的工作。线程不是免费的 - 只是因为你创建一个并不意味着CPU本身有时间对它做任何事情。线程创建和管理的开销通常意味着效率损失。
老派事件循环模型消除了这种开销。当事情变得非常糟糕,就像你的代码中有一个无限循环一样,它们的行为非常糟糕 - 通常会完全锁定。但是当事情进展顺利时,它们实际上可以更快,许多基准测试表明,编写良好的NodeJS模块可以比其他语言中的类似模块表现得更好甚至更好。
总之,NodeJS中最常见的混淆是“异步”的真正含义。想一想的好方法是,在线程模型中,程序员应该是“糟糕”/简单化(写入阻塞代码并等待事情返回),核心虚拟机或操作系统应该是“好”/智能(通过使线程处理异步工作来容忍这一点)。在NodeJS中,程序员应该是“好的”/复杂的(编写结构良好的异步代码),允许JSVM专注于它最擅长的事情,而不需要那么多魔术来使事情运行良好。使用得很好,NodeJS掌握了很多权力。