Node.js 节点如何同时处理多个请求?

Node.js 节点如何同时处理多个请求?,node.js,Node.js,假设我有一个节点应用程序。我有一个API(/听)。现在在这个API的回调中,我有一些代码,它执行一些文件操作,大约需要1分钟。现在,client1点击this/listenapi,节点正忙于执行回调(我知道的是,节点将把文件操作分配给操作系统内核,节点将处于空闲状态)。同时,client2也会访问相同的API,节点会启动第二个client2的文件操作。假设client2的文件操作在client1之前完成。节点如何知道向哪个客户端发送哪个响应 第二个问题:若节点正在执行一些大型计算,而其线程正忙于

假设我有一个节点应用程序。我有一个API(/听)。现在在这个API的回调中,我有一些代码,它执行一些文件操作,大约需要1分钟。现在,client1点击this/listenapi,节点正忙于执行回调(我知道的是,节点将把文件操作分配给操作系统内核,节点将处于空闲状态)。同时,client2也会访问相同的API,节点会启动第二个client2的文件操作。假设client2的文件操作在client1之前完成。节点如何知道向哪个客户端发送哪个响应

第二个问题:若节点正在执行一些大型计算,而其线程正忙于执行这些计算,那个么它会在那个时候接受任何传入的请求吗。如果多个用户同时访问同一个API,会发生什么情况


提前感谢

Node.js在引擎盖下运行一个事件循环
这意味着无论它做什么。为传入连接轮询linux套接字,在为传入数据进行连接之后,在发生事件时执行回调所有事情都发生在单线程循环上
参考:

在您的示例中,对客户端的响应不是由节点完成的。它使用底层的libuv eventloop框架来处理这些()

libuv如何执行此操作?
由于libuv是跨平台的非阻塞事件驱动IO,因此它使用操作系统提供的轮询机制(linux中的epoll、macOS中的kqueue、windows中的iocp)。 从libuv文档:

该库提供的不仅仅是对不同I/O轮询机制的简单抽象:“句柄”和“流”为套接字和其他实体提供了高级抽象;此外,还提供了跨平台文件I/O和线程功能

让我们以linux为例 每当有一个新的客户端连接时,只要服务器(在我们的例子中是libuv)接受连接,内核就会为每个客户端创建一个新的文件描述符,libuv就会将其抽象为一个更高级别的连接句柄(独特的组合,如客户端ip+端口)。 因此,基本上,node执行回调,然后调用libuvapi,该API写入linux为该客户机提供的文件描述符(client1和client2是分开的)

使用linux epoll以scratch编写的TCP服务器示例:

libuv在操作系统平台上提供相同的API,基本上执行上述操作

但是,文件I/O是另一种动物。由于没有干净的跨平台文件i/o轮询机制,libuv求助于线程池机制。它在一个池中打开一些线程,并跨多个线程复用工作负载

再次从libuv文档:

与网络I/O不同,libuv没有可以依赖的特定于平台的文件I/O原语,因此当前的方法是在线程池中运行阻塞文件I/O操作

更多详细信息:
uv\u-tcp\t

上述API用于所有tcp操作,如tcp服务器绑定、连接到客户端、关闭套接字

该句柄是streams句柄的子类(
uv\u stream\t

基本上,通过文件描述符(套接字、管道、tty等)进行的所有读/写操作都由上述API处理

关于第二个问题: 如果节点忙于处理,则不会立即获取第二个请求。但是,请求被写入文件描述符,数据在内核中缓冲,当Node通过libuv轮询每个套接字上的更多数据时(在事件循环阶段),它通过该描述符从内核获取数据,并且只有在调用相应的回调时,数据才可用于Node.js运行时

从node.js设计文档:

由于这些操作中的任何一个都可能会安排更多的操作,并且在轮询阶段处理的新事件由内核排队,因此可以在处理轮询事件时对轮询事件排队。因此,长时间运行的回调允许轮询阶段的运行时间远远超过计时器的阈值。有关更多详细信息,请参阅计时器和轮询部分