【问题标题】:What happens to nodejs server requests when the process is blocked进程被阻塞时nodejs服务器请求会发生什么
【发布时间】:2020-10-14 23:39:10
【问题描述】:

当 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 在此处描述的压力情况下会做什么。

【问题讨论】:

    标签: node.js http server backpressure


    【解决方案1】:

    在操作系统中,如果主机应用程序太忙而无法立即获取,TCP 堆栈有一个等待传入数据或连接的队列,等待相应主机应用程序获取。根据操作系统和配置,入站队列会在某个时候填满,尝试连接的客户端会出错。我不知道 nodejs 中有任何单独的线程将这些线程拾取到自己的队列中,并且 nodejs 可能没有任何理由这样做,因为 TCP 堆栈已经自行实现了入站连接队列。

    如果您阻塞 nodejs 进程的时间足以让 10,000 个传入请求到达,那么您会遇到更大的问题,需要从核心解决阻塞问题。 Nodejs 有线程、子进程和集群,所有这些都可以用来缓解阻塞计算。

    对于在现有的、已打开的 TCP 连接上发送的数据,存在背压(在 TCP 级别)。对于新的传入连接,实际上没有背压之类的东西。新的传入连接要么被接受,要么不被接受。这是我们有时观察到 ERR_CONNECTION_REFUSED 的原因之一。

    这里有一些相关的讨论:What can be the reason of connection refused errors

    节点是否使用一些低级 c++ 读取请求并将其放入队列中?这里涉及背压吗?

    Node 本身不这样做(我知道)。操作系统 TCP 堆栈有一个用于入站数据和传入连接请求的队列。

    这里涉及到unix内核吗?它是否知道在服务器拒绝响应时将请求放入某种队列?

    TCP 堆栈(在 OS 中)确实为到达现有连接的传入数据和入站连接请求提供了一个队列。这个队列的大小是有限的(部分可配置)。

    是否就像 curl 无限期地等待来自套接字的响应一样简单?

    没有。如果服务器上的入站连接请求队列已满,则连接请求将被拒绝。如果队列未满,则只需等待足够长的时间以使其成功。大多数客户端库会使用某种超时并在一段时间后放弃,以防发生导致永远不会发回响应的事情。

    如果服务器被阻塞并且有 10,000 个新请求到达服务器会发生什么?一旦服务器畅通,它们都会得到服务吗? (假设客户端和服务器之间没有负载均衡器或其他超时机制)

    目标主机将对入站连接请求进行排队,直至达到某个限制(取决于操作系统和配置),然后拒绝之后的请求。


    其他一些相关文章:

    How TCP backlog works in Linux

    What is "backlog" in TCP connections?

    TCP Connection Backlog and a Struggling Server

    您阅读这些类型的文章越多,您就越会发现在快速接受大量连接和防御各种类型的 DOS 攻击之间进行权衡。看来需要权衡取舍了。

    【讨论】:

    • 谢谢!这对我来说很有意义。我创建了一个客户端来向上述服务器发送垃圾邮件,并且有一些有趣的发现。我可以在我的 Mac 上产生 500 到 1000 个并行请求之间的任何地方的ECONNRESET(这是 tcp 拒绝的 nodejs 请求)。找出 tcp backlog 有多大,以及有多少开放槽似乎更重要。不过,这是很好的信息,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 2021-06-10
    相关资源
    最近更新 更多