使node.js在出错时不退出

Rob*_*ohr 55 node.js

我正在使用Socket.IO在面向websocket的node.js服务器上工作.我注意到一个错误,某些浏览器没有遵循正确的连接过程到服务器,并且代码没有被写入以优雅地处理它,简而言之,它调用一个方法到一个从未设置的对象,从而杀死服务器由于错误.

我关心的不是特别是bug,而是当发生这样的错误时,整个服务器都会崩溃.有什么我可以在节点的全局级别上做到这一点,如果发生错误它只会记录一条消息,可能会杀死事件,但服务器进程将继续运行?

我不希望其他用户的连接因为一个聪明的用户在大型包含的代码库中利用未被捕获的错误而关闭.

Ivo*_*zel 70

您可以将侦听器附加到uncaughtException进程对象的事件.

代码取自实际的Node.js API参考(它是"process"下的第二项):

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ', err);
});

setTimeout(function () {
  console.log('This will still run.');
}, 500);

// Intentionally cause an exception, but don't catch it.
nonexistentFunc();
console.log('This will not run.');
Run Code Online (Sandbox Code Playgroud)

你现在要做的就是记录它或用它做一些事情,如果你知道在什么情况下发生了bug,你应该在Socket.IO的GitHub页面上提交一个bug:https:
//github.com/ LearnBoost/Socket.IO节点/问题

  • 你可以打印出`err.stack`,它会给你一个堆栈跟踪,其中也包含行号. (5认同)
  • 请注意,这是不好的做法,可能会导致意外行为.请参阅下面的答案. (3认同)

Rud*_*ing 33

使用uncaughtException是一个非常糟糕的主意.

最好的选择是在Node.js 0.8中使用域.如果您使用的是早期版本的Node.js而是永远使用重新启动进程,甚至更好地使用节点集群来生成多个工作进程,并在发生uncaughtException事件时重新启动工作程序.

来自:http://nodejs.org/api/process.html#process_event_uncaughtexception

警告:正确使用'uncaughtException'

请注意,'uncaughtException'是异常处理的粗略机制,旨在仅用作最后的手段.该事件不应用作On Error Resume Next的等效项.未处理的异常本身意味着应用程序处于未定义状态.尝试在未正确恢复异常的情况下恢复应用程序代码可能会导致其他无法预料和不可预测的问题.

将不会捕获从事件处理程序中抛出的异常.相反,该过程将以非零退出代码退出,并且将打印堆栈跟踪.这是为了避免无限递归.

在未被捕获的异常之后尝试正常恢复可能类似于在升级计算机时拔出电源线 - 十分之九没有任何事情发生 - 但是第10次,系统被破坏.

正确使用'uncaughtException'是在关闭进程之前执行已分配资源(例如文件描述符,句柄等)的同步清理.'uncaughtException'后恢复正常操作是不安全的.

要以更可靠的方式重新启动崩溃的应用程序,无论是否发出uncaughtException,都应在单独的进程中使用外部监视器来检测应用程序故障并根据需要进行恢复或重新启动.

  • 我认为这个答案需要某种关于如何使用域的示例以及它们如何解决这个问题. (16认同)
  • 对不起,仍然不同意你的答案为什么......你没有解释为什么域使用更好......原因是 - 它不是......域使用只是允许你包装更小的代码部分(而不是而不是整个过程)来捕获未捕获的异常。但是,如果您的整个代码都在一个域中,则它与未捕获的异常相同。所以根据这个问题,你的回答“这不好,这很好”在我看来是无关紧要的。https://nodejs.org/api/domain.html#domain_warning_don_t_ignore_errors (2认同)
  • 未定义的异常将您的应用程序置于未定义状态,"解决"此问题的唯一好方法是重新启动它.域的作用是更好地包含这种未定义的状态.在未捕获的异常之后,必须通过返回错误并停止进一步执行代码来"重新启动"特定域.因此未被捕获的异常仍然很糟糕,但域限制了它们的影响并且可能允许您丢弃更少的数据.例如,您可以丢弃生成未捕获异常的传入请求并返回错误,但其他100个待处理请求仍可继续. (2认同)

d51*_*512 6

我刚刚对此进行了大量研究(参见此处,此处,此处此处),您的问题的答案是Node不会允许您编写一个错误处理程序来捕获可能发生在您的每个错误情况系统.

express这样的一些框架将允许您捕获某些类型的错误(当异步方法返回错误对象时),但是还有其他条件无法用全局错误处理程序捕获.这是Node的限制(在我看来),一般来说可能是异步编程所固有的.

例如,假设您有以下快递处理程序:

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        if(err)
            next(err);
        else
            res.send("yay");
    });
});
Run Code Online (Sandbox Code Playgroud)

假设文件"some/file"实际上并不存在.在这种情况下,fs.readFile将返回错误作为回调方法的第一个参数.如果你检查它并在它发生时做下一个(错误),默认的快速错误处理程序将接管并做你做的任何事情(例如,向用户返回500).这是处理错误的优雅方式.当然,如果你忘了打电话next(err),它就行不通了.

这是全局处理程序可以处理的错误条件,但考虑另一种情况:

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        if(err)
            next(err);
        else {
            nullObject.someMethod(); //throws a null reference exception
            res.send("yay");
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果您的代码导致您在null对象上调用方法,则会出现错误.这里将抛出异常,它不会被全局错误处理程序捕获,并且您的节点应用程序将终止.当前正在执行该服务请求的所有客户端将突然断开连接,无法解释原因.不适度.

Node中目前没有全局错误处理程序功能来处理这种情况.你不能try/catch在你的所有快速处理程序周围放置一个巨人,因为在执行asyn回调时,这些try/catch块不再在范围内.这只是异步代码的本质,它打破了try/catch错误处理范例.

AFAIK,你唯一的办法是try/catch在每个异步回调中围绕代码的同步部分放置块,如下所示:

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        if(err) {
            next(err);
        }
        else {
            try {
                nullObject.someMethod(); //throws a null reference exception
                res.send("yay");
            }
            catch(e) {
                res.send(500);
            }
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

这将会产生一些讨厌的代码,特别是一旦你开始进入嵌套的异步调用.

有些人认为Node在这些情况下(即死亡)的作用是正确的,因为你的系统处于不一致的状态而你没有其他选择.我不同意这种推理,但我不会进入关于它的哲学辩论.重点是,使用Node,您的选项有很多try/catch小块,或者希望您的测试覆盖率足够好,以免发生这种情况.您可以使用诸如暴发户主管之类的东西来重启您的应用程序,但这只是缓解问题,而不是解决方案.

Node.js目前有一个名为域的不稳定功能,似乎可以解决这个问题,尽管我对此并不了解.