在生产中运行我的Express应用程序时,我希望在其进程被终止时(即发送SIGTERM或SIGINT)正常关闭服务器.
这是我的代码的简化版本:
const express = require('express');
const app = express();
app.get('/', (req, res) => res.json({ ping: true }));
const server = app.listen(3000, () => console.log('Running…'));
setInterval(() => server.getConnections(
(err, connections) => console.log(`${connections} connections currently open`)
), 1000);
process.on('SIGTERM', shutDown);
process.on('SIGINT', shutDown);
function shutDown() {
console.log('Received kill signal, shutting down gracefully');
server.close(() => {
console.log('Closed out remaining connections');
process.exit(0);
});
setTimeout(() => {
console.error('Could not close connections in time, forcefully shutting down');
process.exit(1);
}, 10000);
}
Run Code Online (Sandbox Code Playgroud)
当我在浏览器中运行它并调用URL http:// localhost:3000 /时,setInterval函数中的日志语句将继续打印"1个当前打开的连接",直到我实际关闭浏览器窗口.即使关闭标签,显然也会保持连接打开.
所以,我按Ctrl + C杀了我的服务器,它将进入超时并在10秒后打印"无法关闭连接",同时继续打印"1个连接打开".
只有在我杀死进程之前关闭浏览器窗口时,才会收到"关闭剩余连接"消息.
我在这里错过了什么?正常关闭Express服务器的正确方法是什么?
Pat*_*und 33
如果有人有兴趣,我自己找到了解决方案(很想听到评论中的反馈).
我为服务器上的连接添加了一个监听器,在数组中存储对这些连接的引用.关闭连接后,它们将从阵列中删除.
当服务器被终止时,通过调用其end方法来关闭每个连接.对于某些浏览器(例如Chrome),这还不够,所以在超时后,我会调用destroy每个连接.
const express = require('express');
const app = express();
app.get('/', (req, res) => res.json({ ping: true }));
const server = app.listen(3000, () => console.log('Running…'));
setInterval(() => server.getConnections(
(err, connections) => console.log(`${connections} connections currently open`)
), 1000);
process.on('SIGTERM', shutDown);
process.on('SIGINT', shutDown);
let connections = [];
server.on('connection', connection => {
connections.push(connection);
connection.on('close', () => connections = connections.filter(curr => curr !== connection));
});
function shutDown() {
console.log('Received kill signal, shutting down gracefully');
server.close(() => {
console.log('Closed out remaining connections');
process.exit(0);
});
setTimeout(() => {
console.error('Could not close connections in time, forcefully shutting down');
process.exit(1);
}, 10000);
connections.forEach(curr => curr.end());
setTimeout(() => connections.forEach(curr => curr.destroy()), 5000);
}
Run Code Online (Sandbox Code Playgroud)
Sla*_* II 10
您遇到的问题是所有现代浏览器都会为多个请求重复使用单个连接.这称为保持活动连接.
处理此问题的正确方法是监控所有新连接和请求,并跟踪每个连接的状态(现在是空闲还是活动).然后,您可以强制关闭所有空闲连接,并确保在处理当前请求后关闭活动连接.
我已经实现了@ moebius/http-graceful-shutdown模块,专门用于优雅地关闭整个Express应用程序和节点服务器.遗憾的是,也没有Express,也没有Node本身没有内置的功能.
以下是它如何与任何Express应用程序一起使用:
const express = require('express');
const GracefulShutdownManager = require('@moebius/http-graceful-shutdown').GracefulShutdownManager;
const app = express();
const server = app.listen(8080);
const shutdownManager = new GracefulShutdownManager(server);
process.on('SIGTERM', () => {
shutdownManager.terminate(() => {
console.log('Server is gracefully terminated');
});
});
Run Code Online (Sandbox Code Playgroud)
随意退房模块,GitHub页面有更多细节.
Express 的创建者推荐的开源项目https://github.com/godaddy/terminus ( https://expressjs.com/en/advanced/healthcheck-graceful-shutdown.html )。
终端使用的基本示例:
const http = require('http');
const express = require('express');
const terminus = require('@godaddy/terminus');
const app = express();
app.get('/', (req, res) => {
res.send('ok');
});
const server = http.createServer(app);
function onSignal() {
console.log('server is starting cleanup');
// start cleanup of resource, like databases or file descriptors
}
async function onHealthCheck() {
// checks if the system is healthy, like the db connection is live
// resolves, if health, rejects if not
}
terminus(server, {
signal: 'SIGINT',
healthChecks: {
'/healthcheck': onHealthCheck,
},
onSignal
});
server.listen(3000);
Run Code Online (Sandbox Code Playgroud)
如果您需要服务器生命周期回调(即从服务注册表中注销实例等),terminus 有很多选项:
const options = {
// healtcheck options
healthChecks: {
'/healthcheck': healthCheck // a promise returning function indicating service health
},
// cleanup options
timeout: 1000, // [optional = 1000] number of milliseconds before forcefull exiting
signal, // [optional = 'SIGTERM'] what signal to listen for relative to shutdown
signals, // [optional = []] array of signals to listen for relative to shutdown
beforeShutdown, // [optional] called before the HTTP server starts its shutdown
onSignal, // [optional] cleanup function, returning a promise (used to be onSigterm)
onShutdown, // [optional] called right before exiting
// both
logger // [optional] logger function to be called with errors
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
21110 次 |
| 最近记录: |