我可以想象 100 个请求到达单个 Node.js 服务器的情况。它们中的每一个都需要一些 DB 交互,这些交互是通过一些本机异步代码实现的 - 使用任务队列或至少微任务队列(例如 DB 驱动程序接口被承诺)。
当请求处理程序停止同步时,Node.js 如何返回响应?这 100 个来自描述的请求来自 api/web 客户端的连接会发生什么?
此功能在操作系统级别可用,称为(有趣的是)异步 I/O 或非阻塞 I/O(Windows 也调用/称为重叠 I/O)。
在最低级别,在 C(C#/Swift)中,操作系统提供了一个 API 来跟踪请求和响应。有多种 API 可用,具体取决于您使用的操作系统,Node.js 使用libuv在编译时自动选择最佳可用 API,但为了理解异步 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 活动并执行适当的代码。
毫无疑问,您可以轻松地发现如何概括这种代码模式:您可以将所有挂起的异步请求和回调保存在列表或数组中,并在select()
. 这实际上是 Node.js(以及一般的 javascript)所做的。正是这个回调/文件描述符列表有时被称为事件队列——它本身并不是一个队列,只是你等待执行的事情的集合。
该select()
函数也有一个能用来实现最终超时参数setTimeout()
,并setInterval()
在浏览器中处理GUI事件,使我们可以在等待I / O运行代码。因为记住,select
是阻塞的——我们只能在 select 返回时运行其他代码。通过仔细管理计时器,我们可以计算适当的值作为超时传递给select
。
该fd_set
数据结构是不实际的链接列表。在较旧的实现中,它是一个位域。更现代的实现可以改进位域,只要它符合 API。但是,这部分解释了为什么有这么多的竞争就像异步API poll
,epoll
,kqueue
等他们的建立是为了克服的局限性select
。不同的 API 以不同的方式跟踪文件描述符,一些使用链表,一些哈希表,一些迎合可扩展性(能够侦听数万个套接字)和一些迎合速度,并且大多数尝试比其他人做得更好. 无论他们使用什么,最终用于存储请求的只是一个跟踪文件描述符的数据结构。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句