node.js - 在 Node.js 中发生 ERR_STREAM_PREMATURE_CLOSE 错误后释放套接字的正确方法是什么?
问题描述
我的问题是,当我使用自定义 http 代理时,在响应管道期间发生错误时不会释放套接字。
似乎 Node.js 并没有自己做这件事。这就是为什么我试图通过破坏响应和底层套接字来自己释放它,但它不起作用。我尝试过的任何操作都没有预期用agent.destroy()
. 这显然不是解决方案,因为像这样正常运行的套接字正在被破坏。
这引出了我的问题。释放套接字的正确方法是什么?
下面通过一个例子来重现它。
- 运行下面的脚本
node index.js
- 打开 http://localhost:3000/
- 取消下载
- 您将看到套接字永远被占用。
如果你调用 http://localhost:3000/small 你会看到我期望发生的行为。那就是套接字正在被释放。
const https = require("https");
const http = require("http");
const stream = require("stream");
const agent = new https.Agent({
maxSockets: 20,
maxFreeSockets: 10,
keepAlive: true,
keepAliveMsecs: 5000
});
const server = http.createServer((req, res) => {
let url = "https://releases.ubuntu.com/20.04.3/ubuntu-20.04.3-live-server-amd64.iso?_ga=2.138549238.47332115.1635229845-1229485524.1607530765";
if (req.url === "/small") {
url = "https://nodejs.org/dist/v14.18.1/node-v14.18.1-x64.msi";
}
https.get(url, {
agent
}, (stream) => {
stream.pipe(res);
}).on("error", (e) => {
console.error("Got error: " + e.message);
});
const cleanup = stream.finished(res, (error) => {
if (error) {
if (error.code === "ERR_STREAM_PREMATURE_CLOSE") {
console.error("Pipeline ended non gracefully with no explicit error");
// agent.destroy(); -- Don't want to do this!
res.socket.destroy();
res.destroy();
}
} else {
console.info("Stream succeeded.");
}
cleanup();
});
logSockets();
});
const getSocketCountPerHost = (socketGroup) => {
const regexp = /^:+|:+$/g;
const sockets = agent[socketGroup];
return Object.fromEntries(Object.entries(sockets).map(([key, value]) => [key.replace(regexp, ""), Array.isArray(value) ? value.length : value]));
}
const logSockets = () => {
const sockets = getSocketCountPerHost("sockets");
const freeSockets = getSocketCountPerHost("freeSockets");
console.info("httpsAgent.sockets", sockets);
console.info("httpsAgent.freeSockets", freeSockets);
};
server.listen(3000, () => {
console.log("Listening on port 3000");
setInterval(logSockets, 10_000)
});
先决条件:
解决方案
好的,我自己搞定了。关键是从 获取请求https.Agent
并在它过早关闭时将其销毁。
const clientRequest = https.get(url, {
agent
}, (stream) => {
stream.pipe(res);
}).on("error", (e) => {
console.error("Got error: " + e.message);
});
const cleanup = stream.finished(res, (error) => {
if (error) {
if (error.code === "ERR_STREAM_PREMATURE_CLOSE") {
console.error("Pipeline ended non gracefully with no explicit error");
clientRequest.destroy();
}
} else {
console.info("Stream succeeded.");
}
cleanup();
});
推荐阅读
- php - 有没有办法从单个页面导出 CSS 和 HTML 代码?
- docker - Docker 重复层被推送
- sql - 如何将一个表中的多个列连接到另一个查找表?
- angular - 如何在 Ionic 4 firebase PWA 应用程序中将 promise 转换为 observable
- mysql - bash shell mysql 命令从命令行运行,但不在脚本内部
- c# - 如何显示列表的内容
在 ComboBox 中使用 Caliburn.micro? - machine-learning - 使用 PyTorch 与模型的正向传递并行执行另一个模型
- javascript - Material-UI v1 Drawer 组件在 iOS 13 beta 版本中不起作用
- c# - 在 TabControl 内切换时显示设置为 Visible=false 的项目
- html - 如何在不破坏设计的情况下在 UL 选项卡周围添加边框