问题 Nodejs / express,优雅地关闭


我有一个nodejs服务器运行express来服务器输出http调用。如果出现问题,是否有建议的方法来优雅地关闭服务器?我应该以某种方式让服务器运行吗?

I.E.在一个未被捕获的异常上,服务器就停止了,我认为这会杀死连接的客户端,而不是将它们交给响应者。

我是不是该:

  1. 在允许服务器死亡(然后重新启动)之前,等待所有http连接完成。
  2. 或者我应该尝试阻止服务器死?

这是对的吗?

process.on('exit', function () {
  console.log('About to exit, waiting for remaining connections to complete');
  app.close();
});

在这种情况下,可能会抛出错误,使服务器处于未定义状态,服务器将继续完成剩余连接。

是否有任何好方法来处理错误继续运行或我应该让服务器死并重启?


8128
2018-06-30 18:58


起源



答案:


不要尝试用未处理的异常做任何事情。让服务器死掉。

通常,如果路由处理程序抛出异常,Express将只是捕获它并将HTTP 500返回给客户端。由于Express捕获了异常,因此您的服务器不会崩溃。

关闭服务器的通常情况是在回调中抛出异常时;例如:

app.get('/foo', function(req, res) {
    db.query(..., function(err, r) {
        throw new Error(); // oops, we'll crash
    });
});

因为回调自然地在Express路由器调用堆栈之外执行,所以无法将其与特定请求相关联。但是,你可以防范这种情况:

app.get('/foo', function(req, res, next) {
    db.query(..., function(err, r) {
        try {
            throw new Error();
        } catch(ex) {
            next(ex);
        }
    });
});

表现' next 函数将错误作为其第一个参数。如果你打电话 next 如果有错误参数,它将停止处理路由并查找 错误处理程序,或者只返回500。

当然,包装 一切 在 try/catch 可能是矫枉过正;大多数事情实际上并没有抛出异常。你应该只在你这样做 知道 有些事可能会抛出否则,您可能会因吞咽异常而陷入非常难以调试的不一致状态。

重要的是要记住,一旦抛出意外异常,您的应用程序就处于未定义状态。问题请求可能永远不会完成,您的应用程序永远不会重新启动。或者可能发生任何其他奇怪的事情。 (这是数据库问题吗?文件系统?内存损坏?)

这些情况难以诊断,更不用说调试了。快速失败,崩溃并快速重启服务器会更安全,也可以说更好。是的,任何碰巧连接的客户都会被切断,但是他们只需重试即可。

如果您担心可用性,请使用 集群模块 这样你就有几个(通常是CPU核心数)服务器进程在运行,而一个崩溃不会导致整个站点崩溃。


8
2017-07-01 03:33



这是一个很好的解释。但有一点我还不清楚。 “重要的是要记住,一旦抛出意外异常,您的应用程序就处于未定义状态。问题请求可能永远不会完成,您的应用程序永远不会重启'。这听起来好像我不应该调用app.close();在流程'退出'。真正? - lostintranslation
调用 app.close() 在里面 process  exit 处理程序是没有意义的: 文档说  “'退出'回调结束后,主事件循环将不再运行。”  换句话说, exit 事件告诉你这个过程 将 函数返回后结束。关闭网络监听器是没有意义的,因为没有下一个勾选;接受传入连接的代码永远不会有机会运行。 - josh3736
太好了,谢谢! - lostintranslation
我很确定你必须听 SIGTERM 并打电话 server.close() 那里。读 本教程 详情。 - incarnate
@incarnate:你不会得到一个 SIGTERM 如果您的服务器由于未处理的异常而停止运行 - josh3736


答案:


不要尝试用未处理的异常做任何事情。让服务器死掉。

通常,如果路由处理程序抛出异常,Express将只是捕获它并将HTTP 500返回给客户端。由于Express捕获了异常,因此您的服务器不会崩溃。

关闭服务器的通常情况是在回调中抛出异常时;例如:

app.get('/foo', function(req, res) {
    db.query(..., function(err, r) {
        throw new Error(); // oops, we'll crash
    });
});

因为回调自然地在Express路由器调用堆栈之外执行,所以无法将其与特定请求相关联。但是,你可以防范这种情况:

app.get('/foo', function(req, res, next) {
    db.query(..., function(err, r) {
        try {
            throw new Error();
        } catch(ex) {
            next(ex);
        }
    });
});

表现' next 函数将错误作为其第一个参数。如果你打电话 next 如果有错误参数,它将停止处理路由并查找 错误处理程序,或者只返回500。

当然,包装 一切 在 try/catch 可能是矫枉过正;大多数事情实际上并没有抛出异常。你应该只在你这样做 知道 有些事可能会抛出否则,您可能会因吞咽异常而陷入非常难以调试的不一致状态。

重要的是要记住,一旦抛出意外异常,您的应用程序就处于未定义状态。问题请求可能永远不会完成,您的应用程序永远不会重新启动。或者可能发生任何其他奇怪的事情。 (这是数据库问题吗?文件系统?内存损坏?)

这些情况难以诊断,更不用说调试了。快速失败,崩溃并快速重启服务器会更安全,也可以说更好。是的,任何碰巧连接的客户都会被切断,但是他们只需重试即可。

如果您担心可用性,请使用 集群模块 这样你就有几个(通常是CPU核心数)服务器进程在运行,而一个崩溃不会导致整个站点崩溃。


8
2017-07-01 03:33



这是一个很好的解释。但有一点我还不清楚。 “重要的是要记住,一旦抛出意外异常,您的应用程序就处于未定义状态。问题请求可能永远不会完成,您的应用程序永远不会重启'。这听起来好像我不应该调用app.close();在流程'退出'。真正? - lostintranslation
调用 app.close() 在里面 process  exit 处理程序是没有意义的: 文档说  “'退出'回调结束后,主事件循环将不再运行。”  换句话说, exit 事件告诉你这个过程 将 函数返回后结束。关闭网络监听器是没有意义的,因为没有下一个勾选;接受传入连接的代码永远不会有机会运行。 - josh3736
太好了,谢谢! - lostintranslation
我很确定你必须听 SIGTERM 并打电话 server.close() 那里。读 本教程 详情。 - incarnate
@incarnate:你不会得到一个 SIGTERM 如果您的服务器由于未处理的异常而停止运行 - josh3736


当NodeJS不期待任何事情发生时,它将关闭;当http服务器正在等待连接节点保持运行时。

server.close([callback])

停止服务器接受新连接并保持现有状态   连接。这个函数是异步的,服务器终于来了   当所有连接都结束并且服务器发出一个连接时关闭 close   事件。或者,您可以传递回调来监听 close   事件。

执行此操作后,服务器将继续运行,直到最后一次连接完成,然后正常关闭。

如果您不想等待连接终止,您可以使用 socket.end() 要么 socket.destroy() 在你剩下的连接上。

看到 http://nodejs.org/api/net.html


process.on('exit', function () {
    console.log('About to exit, waiting for remaining connections to complete');
    app.close();
});

此节点应该在节点存在时运行,而不是实际告诉节点退出。这是您可以结束流程的一种方式,但它不会优雅地结束:

process.exit([code])

使用指定的代码结束进程。如果省略,则退出使用   '成功'代码 0

要使用“失败”代码退出:

process.exit(1); 执行节点的shell应该看到退出代码   如 1

看到 http://nodejs.org/api/process.html


我希望这有帮助!


6
2018-06-30 19:45





如果您使用快速,那么您可以使用此npm模块优雅地处理关闭。

https://www.npmjs.com/package/express-graceful-shutdown


0
2018-05-19 10:41



请添加一个显示联盟的节点:)根据npm你是链接插件的维护者。 - geisterfurz007