在Node.js中,当响应为async时,如何处理多个同时请求?

问题描述 投票:1回答:1

我可以想象一下100个请求来到单个Node.js服务器的情况。每一个请求都需要一些DB交互,这就需要一些原生的异步代码来实现--使用任务队列或者至少是微任务队列(例如,DB驱动接口被承诺了)。

当请求处理器停止同步时,Node.js如何返回响应?来自apiweb客户端的连接会发生什么,这100个来自描述的请求是怎么来的?

node.js http httpconnection
1个回答
2
投票

这个功能在操作系统层面是可用的,被称为(有趣的是)异步IO或非阻塞IO(Windows也称其为重叠IO)。

在最底层的C(C#Swift)中,操作系统提供了一个API来跟踪请求和响应。根据你所处的操作系统,有各种API可用,Node.js使用的是 libuv 以在编译时自动选择最佳可用的API,但为了理解异步API的工作原理,让我们来看看所有平台都可用的API:The select() 系统调用。

select() 函数看起来像这样。

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, time *timeout);

The fd_set 数据结构是你感兴趣的文件描述符的集合列表。观看 的IO活动。记住,在POSIX中,套接字也是文件描述符。你使用这个API的方法如下。

// Pseudocode:

// Say you just sent a request to a mysql database and also sent a http
// request to google maps. You are waiting for data to come from both.
// Instead of calling `read()` which would block the thread you add
// the sockets to the read set:

add mysql_socket to readfds
add maps_socket to readfds

// Now you have nothing else to do so you are free to wait for network
// I/O. Great, call select:

select(2, &readfds, NULL, NULL, NULL);

// Select is a blocking call. Yes, non-blocking I/O involves calling a
// blocking function. Yes it sounds ironic but the main difference is
// that we are not blocking waiting for each individual I/O activity,
// we are waiting for ALL of them

// At some point select returns. This is where we check which request
// matches the response:

check readfds if mysql_socket is set {
    then call mysql_handler_callback()
}

check readfds if maps_socket is set {
    then call maps_handler_callback()
}

go to beginning of loop

所以基本上你的问题的答案是 我们检查一个数据结构,检查socketfile刚刚触发了一个IO活动,然后执行相应的代码。

你无疑可以很容易地发现如何将这种代码模式通用化:你可以将所有待处理的异步请求和回调保存在一个列表或数组中,并在前后循环检查,而不是手动设置和检查文件描述符。select(). 事实上,这就是Node.js(以及一般的javascript)所做的事情。而正是这个callbacksfile-descriptors的列表有时被称为事件队列--它本身并不是一个队列,只是你等待执行的事情的集合。

列表中的 select() 函数最后还有一个超时参数,可以用来实现 setTimeout()setInterval() 并在浏览器中处理GUI事件,这样我们就可以在等待IO的时候运行代码。因为要记住。select 是阻塞的--只有当select返回时,我们才能运行其他代码。通过对定时器的仔细管理,我们可以计算出适当的值作为超时时间传递给 select.

fd_set 数据结构实际上不是一个链接列表。在旧的实现中,它是一个位场。更现代的实现只要符合API的要求,就可以在位场上进行改进。但这也部分解释了为什么会有那么多竞争性的async API,如 poll, epoll, kqueue 等。它们的诞生是为了克服 select. 不同的API对文件描述符的跟踪方式不同,有的使用链接列表,有的使用哈希表,有的迎合了可扩展性(能够监听数万个套接字),有的迎合了速度,而大多数API都试图在这两方面做得比其他API更好。无论他们使用什么,最终 用来存储请求的只是一个数据结构,用来记录文件描述符的轨迹。.

© www.soinside.com 2019 - 2024. All rights reserved.