NodeJS(libuv)kqueue():系统中打开的文件太多



我正在尝试使用节点的net模块来实现一个简单的HTTP服务器。当我使用bombardier测试吞吐量时,节点的HTTP模块可以毫无问题地处理每个连接,但net模块会立即失败。我的问题是,节点的http如何处理新连接或连接池?示例:

const http = require("http");
http
.createServer((_, res) => {
res.end();
})
.listen(4000);
const net = require("net");
net
.createServer((socket) => {
socket.end("HTTP/1.0 200 OKrnrn");
})
.listen(4000);

然后我运行bombardier -c 126 -n 1000000 http://localhost:4000http运行得很好,但net抛出:

(libuv) kqueue(): Too many open files in system
node:tty:94
throw new ERR_TTY_INIT_FAILED(ctx);
^
SystemError [ERR_TTY_INIT_FAILED]: TTY initialization failed: uv_tty_init returned ENFILE (file table overflow)
at new SystemError (node:internal/errors:251:5)
at new NodeError (node:internal/errors:336:7)
at new WriteStream (node:tty:94:11)
at createWritableStdioStream (node:internal/bootstrap/switches/is_main_thread:47:16)
at process.getStderr [as stderr] (node:internal/bootstrap/switches/is_main_thread:134:12)
at console.get (node:internal/console/constructor:216:44)
at console.value (node:internal/console/constructor:333:50)
at console.warn (node:internal/console/constructor:368:61)
at Server.<anonymous> (/Users/brielov/Work/Self/speedtest/net.js:8:37)
at Server.emit (node:events:365:28) {
code: 'ERR_TTY_INIT_FAILED',
info: {
errno: -23,
code: 'ENFILE',
me⏎

http模块具有Agent的概念,它实现了一个连接池。如果您没有指定,您将获得全局默认代理(http.globalAgent(。

在更低级的net模块中,您需要自己管理套接字。在您的示例中,避免耗尽套接字的一种方法是在请求完成后销毁套接字:

const net = require("net");
net
.createServer((socket) => {
socket.end("HTTP/1.0 200 OKrnrn");
socket.destroy();
})
.listen(4000);

请注意,从性能的角度来看,总是调用destroy可能不是最佳的。重用连接并实现池是个好主意。但是,要理解为什么最初的实现会迅速耗尽资源,缺少destroy调用应该是原因。在更高抽象上操作的其他库将在后台实现该功能。

处理超过系统套接字限制的另一种方法是通过重写server.maxConnections来限制net模块内部的连接。不建议仅依赖该功能。它将消除您得到的异常,但一旦超过限制,服务器将开始拒绝所有传入的请求。因此,效果是相似的:服务器关闭。

最新更新