node.js 当进程被杀死时,如何优雅地关闭我的 Express 服务器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43003870/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How Do I Shut Down My Express Server Gracefully When Its Process Is Killed?
提问by Patrick Hund
When running my Express application in production, I want to shut down the server gracefully when its process is killed (i.e. a SIGTERM or SIGINT is sent).
在生产中运行我的 Express 应用程序时,我想在其进程被终止(即发送 SIGTERM 或 SIGINT)时优雅地关闭服务器。
Here is a simplified version of my code:
这是我的代码的简化版本:
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);
}
When I run it and call the URL http://localhost:3000/in a browser, the log statement in the setInterval function will keep printing “1 connection currently open” until I actually close the browser window. Even closing the tab will keep the connection open, apparently.
当我运行它并在浏览器中调用 URL http://localhost:3000/时,setInterval 函数中的日志语句将继续打印“1 个当前打开的连接”,直到我真正关闭浏览器窗口。显然,即使关闭选项卡也会保持连接打开。
So wenn I kill my server by hitting Ctrl+C, it will run into the timeout and print “Could not close connections” after 10 seconds, all the while continuing to print “1 connection open”.
所以我按 Ctrl+C 杀死我的服务器,它会遇到超时并在 10 秒后打印“无法关闭连接”,同时继续打印“1 个连接打开”。
Only if I close the browser window before killing the process I get the “closed out remaining connections” message.
仅当我在终止进程之前关闭浏览器窗口时,才会收到“关闭剩余连接”消息。
What am I missing here? What is the proper way to shut down an Express server gracefully?
我在这里缺少什么?正常关闭 Express 服务器的正确方法是什么?
回答by Patrick Hund
In case anyone is interested, I found a solution myself (would love to hear feedback in comments).
如果有人感兴趣,我自己找到了一个解决方案(很想听到评论中的反馈)。
I added a listener for connections opening on the server, storing references to those connections in an array. When the connections are closed, they are removed from the array.
我为在服务器上打开的连接添加了一个侦听器,将这些连接的引用存储在一个数组中。当连接关闭时,它们将从阵列中删除。
When the server is killed, each of the connection is closed by calling its endmethods. For some browsers (e.g. Chrome), this is not enough, so after a timeout, I call destroyon each connection.
当服务器被杀死时,每个连接都会通过调用其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);
}
回答by Slava Fomin II
The problem you are experiencing is that all modern browsers reuse single connection for multiple requests. This is called keep-alive connections.
您遇到的问题是所有现代浏览器都为多个请求重用单个连接。这称为保持活动连接。
The proper way to handle this is to monitor all new connections and requests and to track status of each connection (is it idle or active right now). Then you can forcefully close all idle connections and make sure to close active connections after current request is being processed.
处理此问题的正确方法是监视所有新连接和请求并跟踪每个连接的状态(现在是空闲还是活动)。然后您可以强制关闭所有空闲连接,并确保在处理当前请求后关闭活动连接。
I've implemented the @moebius/http-graceful-shutdownmodule specifically designed to gracefully shutdown Express applications and Node servers overall. Sadly nor Express, nor Node itself doesn't have this functionality built-in.
我已经实现了@moebius/http-graceful-shutdown模块,该模块专门设计用于从整体上优雅地关闭 Express 应用程序和 Node 服务器。可悲的是,Express 和 Node 本身都没有内置此功能。
Here's how it can be used with any Express application:
以下是它可以与任何 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');
});
});
Feel free to check-out the module, the GitHub page has more details.
请随意查看模块,GitHub 页面有更多详细信息。
回答by Przemek Nowak
There is open source project https://github.com/godaddy/terminusrecommended by the creators of Express (https://expressjs.com/en/advanced/healthcheck-graceful-shutdown.html).
Express 的创建者推荐的开源项目https://github.com/godaddy/terminus( https://expressjs.com/en/advanced/healthcheck-graceful-shutdown.html)。
The basic example of terminus usage:
终端使用的基本示例:
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);
terminus has a lot of options in case you need server lifecycle callbacks (ie. to deregister instance from service registry, etc.):
如果您需要服务器生命周期回调(即从服务注册表中注销实例等),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
};
回答by twg
Try the NPM express-graceful-shutdown module, Graceful shutdown will allow any connections including to your DB to finish, not allow any fresh/new ones to be established. Since you are working with express that may be the module you are looking for, however a quick NPM search will reveal a whole list of modules suited to Http servers etc.
尝试 NPM express-graceful-shutdown 模块,优雅关闭将允许完成任何连接,包括与您的数据库的连接,不允许建立任何新的/新的连接。由于您正在使用 express 可能是您正在寻找的模块,但是快速 NPM 搜索将显示适合 Http 服务器等的完整模块列表。
回答by Anthony Hildoer
Handle OS signals correctly: https://www.npmjs.com/package/daemonix
正确处理操作系统信号:https: //www.npmjs.com/package/daemonix
Gracefully shutdown Express: https://www.npmjs.com/package/@stringstack/expresshttps://www.npmjs.com/package/@stringstack/core
优雅地关闭 Express:https: //www.npmjs.com/package/@stringstack/express https://www.npmjs.com/package/@stringstack/core
This combination of tools will stop new connections on shutdown, allow existing connections to finish, and then finally exit.
这种工具组合将在关闭时停止新连接,允许完成现有连接,然后最终退出。

