假设我有一个节点应用程序。我有一个API(/ listen)。现在在此API的回调中,我有执行一些文件操作的代码,大约需要1分钟。现在,client1命中此/ listen API,节点正忙于执行回调(据我所知,节点将把文件操作分配给os内核,节点将处于闲置状态)。同时,client2也使用相同的API,并且节点启动第二个client2的文件操作。假设client2的文件操作在client1之前完成。节点将如何知道向哪个客户端发送哪个响应?
第二个问题是节点是否正在执行一些大的计算并且其线程正在忙于这样做,届时它将接受任何传入的请求。如果多个用户同时使用相同的API,将会发生什么?
提前致谢
最佳答案
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,因此它使用操作系统提供的轮询机制(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.html(uv_tcp_t
)
上面的API用于所有tcp操作,例如tcp服务器绑定,连接到客户端,关闭套接字
该句柄是流句柄(uv_stream_t
)的子类
http://docs.libuv.org/en/v1.x/stream.html
基本上所有通过文件描述符(套接字,管道,tty等)的读/写操作都由上述API处理
关于第二个问题:
如果节点正在忙于处理,它将不会立即获得第二个请求。但是,请求将被写入文件描述符,并且数据被缓存在内核中,并且当通过libuv的Node在每个套接字上轮询更多数据时(在其事件循环阶段),它仅通过以下方式从内核中获取该描述符的数据:调用相应的回调,该数据可用于node.js运行时
来自node.js设计文档:
由于这些操作中的任何一个都可能调度更多操作,并且在轮询阶段处理的新事件由内核排队,因此可以在处理轮询事件的同时将轮询事件排队。结果,长时间运行的回调可使轮询阶段运行的时间比计时器的阈值长得多。有关更多详细信息,请参见计时器和轮询部分。
关于node.js - Node 如何同时处理多个请求?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59168067/