Node.js如何使用更少的线程来处理多个连接

How does Node.js use fewer threads to handle multiple connections?

本文关键字:处理 连接 线程 js 何使用 Node      更新时间:2023-09-26

我对事件和回调、同步/异步、调用堆栈和队列没有问题。

然而,据我所知,其他服务器为每个连接创建一个新线程,其中包含阻塞请求和该请求响应的处理程序,在节点中,该处理程序将作为回调传递给主线程。因此,这种服务器处理多个请求的能力受到其创建和在多个线程之间切换的能力的限制。

当Node接收到阻塞请求时,它会将其发送到异步区域,同时继续处理主线程。异步领域会发生什么,难道不需要创建一个线程来等待该请求的响应,然后将事件发送到节点事件循环吗?如果是这样,为什么Node不受服务器创建线程和在线程之间切换的能力的限制?如果没有,请求会发生什么?

我认为对事件循环的实际工作方式有些困惑。NodeJS不会"接收阻塞请求",也不会"将其发送到异步区域"。它一开始是异步的——除非您调用...Sync()模式函数,否则每个调用和每个操作都是异步的。令人困惑的是,一旦进入CODE,每个操作都是同步的。

这是一种"协作多任务"的方法——所有对系统的调用都应该"开始滚动"并立即返回,而您自己的代码则应该尽可能快地完成它需要做的事情,并将控制权交还给JSVM(通过从函数返回)。

要了解在处理网络通信时这是如何工作的,您需要回到线程真正存在之前。在早期,如果您有多个网络连接,那么您的单线程进程必须将它想要的信息(例如"数据是否已到达供我读取?")的所有套接字列表放在一起,并通过调用select()询问操作系统这是否属实。对于每个问题的每个套接字,这将是一个是/否。这通常是在while()循环中完成的,该循环一直运行到程序终止。你会要求提供一份包含新数据的套接字列表,读取这些数据,用它做点什么,然后一次又一次地回到睡眠状态。

NodeJS要复杂得多,但这种类比对它来说很有效。它有一个主要的"事件循环",不断地睡觉,直到有工作要做,然后醒来再做。

你所做的一切都来自或进入这个频道。如果你将数据写入网络套接字,并要求在完成后得到通知(回叫),NodeJS会将你的请求传递给操作系统,然后进入睡眠状态。你停止跑步。您的上下文已保存-所有本地变量都已保存。当操作系统返回并说"完成!"时,NodeJS会检查它的列表,看到你想知道这一点,并调用你的函数,重新加载你的上下文,这样你所有的本地变量都在你需要的地方。

非常清楚的是,当数据写入网络后,操作系统通知回来时,NodeJS完全有可能忙于其他工作!NodeJS不会"创建线程"来处理它——它会完全忽略它,直到它有空闲时间!它不会丢失。。。它只是还没有被处理。

这让习惯于对模型进行线程处理的程序员们抓狂——这种"在有机会之前"从不立即响应传入事件的恒定状态可能是有效的,这似乎不合逻辑。但软件体系结构往往是骗人的。线程模型实际上具有相当高的开销。CPU核心数量并不是无限的——整个计算机作为一个整体一直在做大量的工作。线程不是免费的-仅仅因为你创建了一个线程并不意味着CPU本身有时间对它做任何事情。线程创建和管理的开销通常意味着效率的损失。

老式的事件循环模型消除了这种开销。当事情发展得很糟糕时,比如你的代码中有一个无限循环,它们可能会表现得很糟糕——通常会完全锁定。但是,当事情进展顺利时,它们实际上可以更快,许多基准测试已经表明,编写良好的NodeJS模块可以与其他语言中的类似模块一样好,甚至更好。

总之,NodeJS中最常见的混淆是"async"的真正含义。一个很好的想法是,在线程模型中,程序员被认为是"糟糕的"/简单的(编写阻塞代码,然后等待事情返回),而核心VM或OS被期望是"好的"/智能的(通过让线程处理异步工作来容忍这一点)。在NodeJS中,程序员应该是"优秀的"/复杂的(编写结构良好的异步代码),允许JSVM专注于它最擅长的事情,而不需要太多的魔法来让事情正常工作。NodeJS使用得很好,它给你带来了很大的力量。