响应异步时,如何在Node.js中处理多个同时请求?

时间:2020-05-05 05:42:22

标签: node.js http httpconnection

我可以想象单个Node.js服务器有100个请求的情况。它们每个都需要一些DB交互,这些交互是通过使用任务队列或至少使用微任务队列(例如,实现DB驱动程序接口)来实现的一些本地异步代码。

当请求处理程序停止同步时,Node.js如何返回响应?从这100个来自描述的请求发起的api / web客户端连接会发生什么情况?

1 个答案:

答案 0 :(得分:2)

此功能在操作系统级别可用,被称为(很有趣)异步I / O或非阻塞I / O(Windows也称其为重叠I / O)。

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

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

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

fd_set数据结构是您对监视的I / O活动感兴趣的文件描述符的集合/列表。请记住,在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

所以基本上,您的问题的答案是我们检查一个数据结构,是什么套接字/文件刚刚触发了I / O活动并执行适当的代码。

毫无疑问,您可以轻松地找到如何概括此代码模式的方法:无需手动设置和检查文件描述符,您可以将所有待处理的异步请求和回调保留在列表或数组中,并在{{1之前和之后}}。实际上,这就是Node.js(通常是javascript)所做的。这就是回调/文件描述符的列表,有时也称为事件队列-它本身不是队列,只是您等待执行的事情的集合。

select()函数的末尾还有一个超时参数,可用于实现select()setTimeout(),并且在浏览器中处理GUI事件,以便我们可以在等待代码的同时运行代码I / O。因为请记住,setInterval()正在阻止-如果select返回,我们只能运行其他代码。通过精心管理计时器,我们可以计算出适当的值作为超时值传递到select

select数据结构实际上不是链接列表。在较旧的实现中,它是一个位域。只要符合API,更现代的实现就可以改善位域。但这部分解释了为什么竞争的异步API如此之多,例如fd_setpollepoll等。创建它们是为了克服kqueue的局限性。不同的API以不同的方式跟踪文件描述符,一些使用链表,一些哈希表,一些满足可伸缩性(能够监听成千上万个套接字),一些满足速度要求,并且大多数都试图做到更好。不管它们使用什么,最后用于存储请求的内容只是一个跟踪文件描述符的数据结构