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

时间:2019-12-04 02:22:17

标签: node.js

假设我有一个节点应用程序。我有一个API(/ listen)。现在在此API的回调中,我有执行一些文件操作的代码,大约需要1分钟。现在,client1命中此/ listen API,节点正忙于执行回调(据我所知,节点会将文件操作分配给os内核,节点将处于闲置状态)。同时,client2也使用相同的API,并且节点启动第二个client2的文件操作。假设client2的文件操作在client1之前完成。节点将如何知道向哪个客户端发送哪个响应?

第二个问题,如果节点正在执行一些大的计算,并且它的线程正在忙于这样做,那么它将在那时接受任何传入的请求。如果多个用户同时使用相同的API,会发生什么?

预先感谢

1 个答案:

答案 0 :(得分:1)

Node.js在后台运行事件循环。
这意味着它所做的一切。在linux套接字上轮询传入的连接,在传入的数据连接之后,在事件发生时执行回调,一切都在单个线程循环中发生
参考:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

在您的示例中,返回客户端的响应不是由节点完成的。它使用底层的libuv eventloop框架来处理这些(http://docs.libuv.org/en/v1.x/design.html

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

  

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

让我们以linux为例 每当有一个新的客户端连接时,只要服务器接受该连接(在我们的示例中为libuv),内核就会为每个客户端创建一个新的文件描述符,并且libuv将其抽象为更高级别的连接句柄(像客户端ip + port这样的唯一组合) )。 因此,基本上,节点执行回调,然后调用libuv API,该API写入Linux为该客户端提供的文件描述符(客户端1和客户端2分开)

使用linux epoll从头开始编写的示例TCP服务器:
http://swingseagull.github.io/2016/11/08/epoll-sample/

libuv基本上执行上述操作,并在整个OS平台上提供相同的API。

但是文件I / O是另一种动物。由于没有干净的跨平台文件I / O轮询机制,因此libuv诉诸于线程池机制。它在池中打开一些线程,并在整个工作负载之间多路复用

再次从libuv文档中:

  

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

更多详细信息:
http://docs.libuv.org/en/v1.x/tcp.htmluv_tcp_t
上面的API用于所有tcp操作,例如tcp服务器绑定,连接到客户端,关闭套接字

该句柄是流句柄(uv_stream_t)的子类。
http://docs.libuv.org/en/v1.x/stream.html

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

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

来自node.js设计文档:

  

由于这些操作中的任何一个都可能调度更多操作,并且在轮询阶段中处理的新事件已由内核排队,因此可以在处理轮询事件时将轮询事件排队。结果,长时间运行的回调可使轮询阶段运行的时间比计时器的阈值长得多。有关更多详细信息,请参见计时器和轮询部分。