当进程被阻塞时nodejs服务器请求会发生什么
What happens to nodejs server requests when the process is blocked
当 nodejs 服务器被阻止时,传入请求会发生什么情况?有时服务器会被阻塞,因为它正在处理一些计算量大的事情,或者可能在做一些同步 IO(例如写入 sqlite 数据库)。这最好用一个例子来描述:
给定这样的服务器:
const { execSync } = require('child_process')
const express = require('express')
const app = express()
const port = 3000
// a synchronous (blocking) sleep function
function sleep(ms) {
execSync(`sleep ${ms / 1000}`)
}
app.get('/block', (req, res) => {
sleep(req.query.ms)
res.send(`Process blocked for ${req.query.ms}ms.`)
})
app.get('/time', (req, res) => res.send(new Date()))
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
我可以像这样阻止 nodejs 进程:
# block the server for two seconds
curl http://localhost:3000/block\?ms\=2000
并在它被阻止时尝试向服务器发出另一个请求:
curl http://localhost:3000/time
第二个请求将挂起,直到阻塞调用完成,然后以预期的日期时间响应。我的问题是,当 nodejs 进程被阻塞时,请求具体发生了什么?
- 节点是否使用一些低级 C++ 读取请求并将其放入队列中?这里涉及背压吗?
- 这里涉及unix内核吗?它知道在服务器拒绝响应时向某种队列发出请求吗?
- 是否就像 curl 无限期地等待来自套接字的响应一样简单?
- 如果服务器被阻止并且有 10,000 个新请求到达服务器会怎样?一旦服务器畅通,它们都会得到服务吗? (假设客户端和服务器之间没有负载平衡器或其他超时机制)
最后,我知道阻止 nodejs 是不好的做法,但我不是在询问最佳做法。我想了解 nodejs 在此处描述的压力环境下会做什么。
在 OS 中,TCP 堆栈有一个队列用于传入数据或连接,如果主机应用程序太忙而无法立即接收,则等待相应主机应用程序接收。根据 OS 和配置,入站队列会在某个时候填满,尝试连接的客户端会出错。我不知道 nodejs 中有任何单独的线程将它们拾取到自己的队列中,并且 nodejs 可能没有任何理由这样做,因为 TCP 堆栈已经实现了自己的入站连接队列。
如果您阻塞 nodejs 进程的时间足以让 10,000 个传入请求到达,那么您的问题就大得多,需要解决阻塞问题的核心。 Nodejs 有线程、子进程和集群,所有这些都可以用来缓解阻塞计算。
对于在现有的、已打开的 TCP 连接上发送的数据,存在背压(在 TCP 级别)。对于新的传入连接,确实没有背压这样的东西。新的传入连接要么被接受,要么不被接受。这是我们有时观察到 ERR_CONNECTION_REFUSED.
的原因之一
这里有一些相关的讨论:What can be the reason of connection refused errors。
Does node read in the request using some low level c++ and put it into a queue? Is backpressure involved here?
Node 本身不会这样做(据我所知)。 OS TCP 堆栈有一个用于入站数据和传入连接请求的队列。
Is the unix kernel involved here? Does it know to put a request on some kind of queue while a server refuses to respond?
TCP 堆栈(在 OS 中)确实有一个队列,用于到达现有连接的传入数据和入站连接请求。此队列的大小有限(且部分可配置)。
Is it just as simple as curl waiting on a response from a socket indefinitely?
没有。如果服务器上的入站连接请求队列已满,则连接请求将被拒绝。如果队列未满,则只需等待足够长的时间使其成功即可。大多数客户端库将使用某种超时并在一段时间后放弃,以防万一发生导致永远不会发回响应的事情。
What happens if the server is blocked and 10,000 new requests hit the server? Will they all be serviced as soon as the server becomes unblocked? (assuming there is no load balancer or other timeout mechanisms in between the client & server)
目标主机会将入站连接请求排队到某个限制(因 OS 和配置而异),然后拒绝之后的请求。
其他一些相关文章:
How TCP backlog works in Linux
TCP Connection Backlog and a Struggling Server
您阅读的此类文章越多,您就越会发现快速接受大量连接与防御各种类型的 DOS 攻击之间的权衡。看来得权衡一下了。
当 nodejs 服务器被阻止时,传入请求会发生什么情况?有时服务器会被阻塞,因为它正在处理一些计算量大的事情,或者可能在做一些同步 IO(例如写入 sqlite 数据库)。这最好用一个例子来描述:
给定这样的服务器:
const { execSync } = require('child_process')
const express = require('express')
const app = express()
const port = 3000
// a synchronous (blocking) sleep function
function sleep(ms) {
execSync(`sleep ${ms / 1000}`)
}
app.get('/block', (req, res) => {
sleep(req.query.ms)
res.send(`Process blocked for ${req.query.ms}ms.`)
})
app.get('/time', (req, res) => res.send(new Date()))
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
我可以像这样阻止 nodejs 进程:
# block the server for two seconds
curl http://localhost:3000/block\?ms\=2000
并在它被阻止时尝试向服务器发出另一个请求:
curl http://localhost:3000/time
第二个请求将挂起,直到阻塞调用完成,然后以预期的日期时间响应。我的问题是,当 nodejs 进程被阻塞时,请求具体发生了什么?
- 节点是否使用一些低级 C++ 读取请求并将其放入队列中?这里涉及背压吗?
- 这里涉及unix内核吗?它知道在服务器拒绝响应时向某种队列发出请求吗?
- 是否就像 curl 无限期地等待来自套接字的响应一样简单?
- 如果服务器被阻止并且有 10,000 个新请求到达服务器会怎样?一旦服务器畅通,它们都会得到服务吗? (假设客户端和服务器之间没有负载平衡器或其他超时机制)
最后,我知道阻止 nodejs 是不好的做法,但我不是在询问最佳做法。我想了解 nodejs 在此处描述的压力环境下会做什么。
在 OS 中,TCP 堆栈有一个队列用于传入数据或连接,如果主机应用程序太忙而无法立即接收,则等待相应主机应用程序接收。根据 OS 和配置,入站队列会在某个时候填满,尝试连接的客户端会出错。我不知道 nodejs 中有任何单独的线程将它们拾取到自己的队列中,并且 nodejs 可能没有任何理由这样做,因为 TCP 堆栈已经实现了自己的入站连接队列。
如果您阻塞 nodejs 进程的时间足以让 10,000 个传入请求到达,那么您的问题就大得多,需要解决阻塞问题的核心。 Nodejs 有线程、子进程和集群,所有这些都可以用来缓解阻塞计算。
对于在现有的、已打开的 TCP 连接上发送的数据,存在背压(在 TCP 级别)。对于新的传入连接,确实没有背压这样的东西。新的传入连接要么被接受,要么不被接受。这是我们有时观察到 ERR_CONNECTION_REFUSED.
的原因之一这里有一些相关的讨论:What can be the reason of connection refused errors。
Does node read in the request using some low level c++ and put it into a queue? Is backpressure involved here?
Node 本身不会这样做(据我所知)。 OS TCP 堆栈有一个用于入站数据和传入连接请求的队列。
Is the unix kernel involved here? Does it know to put a request on some kind of queue while a server refuses to respond?
TCP 堆栈(在 OS 中)确实有一个队列,用于到达现有连接的传入数据和入站连接请求。此队列的大小有限(且部分可配置)。
Is it just as simple as curl waiting on a response from a socket indefinitely?
没有。如果服务器上的入站连接请求队列已满,则连接请求将被拒绝。如果队列未满,则只需等待足够长的时间使其成功即可。大多数客户端库将使用某种超时并在一段时间后放弃,以防万一发生导致永远不会发回响应的事情。
What happens if the server is blocked and 10,000 new requests hit the server? Will they all be serviced as soon as the server becomes unblocked? (assuming there is no load balancer or other timeout mechanisms in between the client & server)
目标主机会将入站连接请求排队到某个限制(因 OS 和配置而异),然后拒绝之后的请求。
其他一些相关文章:
How TCP backlog works in Linux
TCP Connection Backlog and a Struggling Server
您阅读的此类文章越多,您就越会发现快速接受大量连接与防御各种类型的 DOS 攻击之间的权衡。看来得权衡一下了。