使 node.js 在出错时不退出

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4213351/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 13:44:00  来源:igfitidea点击:

Make node.js not exit on error

node.js

提问by RobKohr

I am working on a websocket oriented node.js server using Socket.IO. I noticed a bug where certain browsers aren't following the correct connect procedure to the server, and the code isn't written to gracefully handle it, and in short, it calls a method to an object that was never set up, thus killing the server due to an error.

我正在使用 Socket.IO 开发面向 websocket 的 node.js 服务器。我注意到一个错误,其中某些浏览器没有遵循正确的连接程序到服务器,并且编写的代码不是为了优雅地处理它,简而言之,它调用一个从未设置过的对象的方法,从而杀死服务器由于错误。

My concern isn't with the bug in particular, but the fact that when such errors occur, the entire server goes down. Is there anything I can do on a global level in node to make it so if an error occurs it will simply log a message, perhaps kill the event, but the server process will keep on running?

我特别关心的不是这个错误,而是当发生此类错误时,整个服务器都会停机。我可以在节点的全局级别上做些什么来使它发生错误,它只会记录一条消息,也许会终止该事件,但服务器进程将继续运行?

I don't want other users' connections to go down due to one clever user exploiting an uncaught error in a large included codebase.

我不希望其他用户的连接因为一个聪明的用户利用包含的大型代码库中未发现的错误而中断。

回答by Ivo Wetzel

You can attach a listener to the uncaughtExceptionevent of the process object.

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

Code taken from the actual Node.js API reference(it's the second item under "process"):

代码取自实际的Node.js API 参考(它是“进程”下的第二项):

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.');

All you've got to do now is to log it or do something with it, in case you know under what circumstances the bug occurs, you should file a bug over at Socket.IO's GitHub page:
https://github.com/LearnBoost/Socket.IO-node/issues

你现在要做的就是记录它或用它做一些事情,如果你知道错误是在什么情况下发生的,你应该在 Socket.IO 的 GitHub 页面上提交错误:https:
//github.com/ LearnBoost/Socket.IO-node/issues

回答by Rudolf Meijering

Using uncaughtException is a very bad idea.

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

The best alternative is to use domains in Node.js 0.8. If you're on an earlier version of Node.js rather use foreverto restart your processes or even better use node clusterto spawn multiple worker processes and restart a worker on the event of an uncaughtException.

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

From: http://nodejs.org/api/process.html#process_event_uncaughtexception

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

Warning: Using 'uncaughtException' correctly

Note that 'uncaughtException' is a crude mechanism for exception handling intended to be used only as a last resort. The event should not be used as an equivalent to On Error Resume Next. Unhandled exceptions inherently mean that an application is in an undefined state. Attempting to resume application code without properly recovering from the exception can cause additional unforeseen and unpredictable issues.

Exceptions thrown from within the event handler will not be caught. Instead the process will exit with a non-zero exit code and the stack trace will be printed. This is to avoid infinite recursion.

Attempting to resume normally after an uncaught exception can be similar to pulling out of the power cord when upgrading a computer -- nine out of ten times nothing happens - but the 10th time, the system becomes corrupted.

The correct use of 'uncaughtException' is to perform synchronous cleanup of allocated resources (e.g. file descriptors, handles, etc) before shutting down the process. It is not safe to resume normal operation after 'uncaughtException'.

To restart a crashed application in a more reliable way, whether uncaughtException is emitted or not, an external monitor should be employed in a separate process to detect application failures and recover or restart as needed.

警告:正确使用“uncaughtException”

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

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

尝试在未捕获的异常后恢复正常类似于升级计算机时拔掉电源线 - 十分之九没有任何反应 - 但第 10 次,系统损坏。

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

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

回答by d512

I just did a bunch of research on this (see here, here, here, and here) and the answer to your question is that Node will not allow you to write one error handler that will catch every error scenario that could possibly occur in your system.

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

Some frameworks like expresswill allow you to catch certain types of errors (when an async method returns an error object), but there are other conditions that you cannot catch with a global error handler. This is a limitation (in my opinion) of Node and possibly inherent to async programming in general.

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

For example, say you have the following express handler:

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

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

Let's say that the file "some/file" does not actually exist. In this case fs.readFile will return an error as the first argument to the callback method. If you check for that and do next(err) when it happens, the default express error handler will take over and do whatever you make it do (e.g. return a 500 to the user). That's a graceful way to handle an error. Of course, if you forget to call next(err), it doesn't work.

假设文件“some/file”实际上并不存在。在这种情况下, fs.readFile 将返回一个错误作为回调方法的第一个参数。如果您检查并在它发生时执行 next(err) ,则默认的快速错误处理程序将接管并执行您所做的任何操作(例如,将 500 返回给用户)。这是处理错误的一种优雅方式。当然,如果您忘记调用next(err),则不起作用。

So that's the error condition that a global handler can deal with, however consider another case:

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

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");
        }
    });
});

In this case, there is a bug if your code that results in you calling a method on a null object. Here an exception will be thrown, it will not be caught by the global error handler, and your node app will terminate. All clients currently executing requests on that service will get suddenly disconnected with no explanation as to why. Ungraceful.

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

There is currently no global error handler functionality in Node to handle this case. You cannot put a giant try/catcharound all your express handlers because by the time your asyn callback executes, those try/catchblocks are no longer in scope. That's just the nature of async code, it breaks the try/catch error handling paradigm.

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

AFAIK, your only recourse here is to put try/catchblocks around the synchronous parts of your code inside each one of your async callbacks, something like this:

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);
            }
        }
    });
});

That's going to make for some nasty code, especially once you start getting into nested async calls.

这会产生一些讨厌的代码,尤其是当您开始使用嵌套的异步调用时。

Some people think that what Node does in these cases (that is, die) is the proper thing to do because your system is in an inconsistent state and you have no other option. I disagree with that reasoning but I won't get into a philosophical debate about it. The point is that with Node, your options are lots of little try/catchblocks or hope that your test coverage is good enough so that this doesn't happen. You can put something like upstartor supervisorin place to restart your app when it goes down but that's simply mitigation of the problem, not a solution.

有些人认为 Node 在这些情况下所做的(即死亡)是正确的做法,因为您的系统处于不一致状态,您别无选择。我不同意这种推理,但我不会就此展开哲学辩论。关键是,对于 Node,您的选择是很多try/catch小块,或者希望您的测试覆盖率足够好,以免发生这种情况。您可以放置​​诸如upstartsupervisor 之类的东西来在应用程序出现故障时重新启动应用程序,但这只是缓解问题,而不是解决方案。

Node.js has a currently unstable feature called domainsthat appears to address this issue, though I don't know much about it.

Node.js 有一个目前不稳定的特性,称为,它似乎可以解决这个问题,尽管我对此知之甚少。

回答by Dean Rather

I've just put together a class which listens for unhandled exceptions, and when it see's one it:

我刚刚组合了一个类来侦听未处理的异常,当它看到一个时:

  • prints the stack trace to the console
  • logs it in it's own logfile
  • emails you the stack trace
  • restarts the server (or kills it, up to you)
  • 将堆栈跟踪打印到控制台
  • 将它记录在它自己的日志文件中
  • 将堆栈跟踪通过电子邮件发送给您
  • 重新启动服务器(或杀死它,由您决定)

It will require a little tweaking for your application as I haven't made it generic as yet, but it's only a few lines and it might be what you're looking for!

它需要对您的应用程序进行一些调整,因为我还没有使它成为通用的,但它只有几行,它可能是您正在寻找的!

Check it out!

一探究竟!

Note:this is over 4 years old at this point, unfinished, and there may now be a better way - I don't know!)

注意:此时这已经超过 4 年了,未完成,现在可能有更好的方法 - 我不知道!)

process.on
(
    'uncaughtException',
    function (err)
    {
        var stack = err.stack;
        var timeout = 1;

        // print note to logger
        logger.log("SERVER CRASHED!");
        // logger.printLastLogs();
        logger.log(err, stack);


        // save log to timestamped logfile
        // var filename = "crash_" + _2.formatDate(new Date()) + ".log";
        // logger.log("LOGGING ERROR TO "+filename);
        // var fs = require('fs');
        // fs.writeFile('logs/'+filename, log);


        // email log to developer
        if(helper.Config.get('email_on_error') == 'true')
        {
            logger.log("EMAILING ERROR");
            require('./Mailer'); // this is a simple wrapper around nodemailer http://documentup.com/andris9/nodemailer/
            helper.Mailer.sendMail("GAMEHUB NODE SERVER CRASHED", stack);
            timeout = 10;
        }

        // Send signal to clients
//      logger.log("EMITTING SERVER DOWN CODE");
//      helper.IO.emit(SIGNALS.SERVER.DOWN, "The server has crashed unexpectedly. Restarting in 10s..");


        // If we exit straight away, the write log and send email operations wont have time to run
        setTimeout
        (
            function()
            {
                logger.log("KILLING PROCESS");
                process.exit();
            },
            // timeout * 1000
            timeout * 100000 // extra time. pm2 auto-restarts on crash...
        );
    }
);

回答by KTys

Had a similar problem. Ivo's answer is good. But how can you catch an error in a loop and continue?

有类似的问题。伊沃的回答很好。但是你怎么能在循环中捕捉错误并继续呢?

var folder='/anyFolder';
fs.readdir(folder, function(err,files){
    for(var i=0; i<files.length; i++){
        var stats = fs.statSync(folder+'/'+files[i]);
    }
});

Here, fs.statSynch throws an error (against a hidden file in Windows that barfs I don't know why). The error can be caught by the process.on(...) trick, but the loop stops.

在这里, fs.statSynch 抛出一个错误(针对 Windows 中的隐藏文件,我不知道为什么)。process.on(...) 技巧可以捕获错误,但循环停止。

I tried adding a handler directly:

我尝试直接添加处理程序:

var stats = fs.statSync(folder+'/'+files[i]).on('error',function(err){console.log(err);});

This did not work either.

这也不起作用。

Adding a try/catch around the questionable fs.statSynch() was the best solution for me:

在有问题的 fs.statSynch() 周围添加 try/catch 对我来说是最好的解决方案:

var stats;
try{
    stats = fs.statSync(path);
}catch(err){console.log(err);}

This then led to the code fix (making a clean path var from folder and file).

然后这导致了代码修复(从文件夹和文件创建一个干净的路径变量)。

回答by Mladen Janjetovic

I found PM2as the best solution for handling node servers, single and multiple instances

我发现PM2是处理节点服务器、单个和多个实例的最佳解决方案

回答by Amandeep Singh

One way of doing this would be spinning the child process and communicate with the parent process via 'message' event.

这样做的一种方法是旋转子进程并通过“消息”事件与父进程通信。

In the child processwhere the error occurs, catch that with 'uncaughtException' to avoid crashing the application. Mind that Exceptions thrown from within the event handler will not be caught. Once the error is caught safely, send a message like: {finish: false}.

在发生错误的子进程中,使用“uncaughtException”捕获该错误以避免应用程序崩溃。请注意,不会捕获从事件处理程序中抛出的异常。一旦错误被安全地捕获,发送如下消息:{finish: false}

Parent Processwould listen to the message event and send the message again to the child process to re-run the function.

父进程会监听消息事件并再次将消息发送到子进程以重新运行该函数。

Child Process:

子进程:

// In child.js
// function causing an exception
  const errorComputation = function() {

        for (let i = 0; i < 50; i ++) {
            console.log('i is.......', i);
            if (i === 25) {
                throw new Error('i = 25');
            }
        }
        process.send({finish: true});
}

// Instead the process will exit with a non-zero exit code and the stack trace will be printed. This is to avoid infinite recursion.
process.on('uncaughtException', err => {
   console.log('uncaught exception..',err.message);
   process.send({finish: false});
});

// listen to the parent process and run the errorComputation again
process.on('message', () => {
    console.log('starting process ...');
    errorComputation();
})

Parent Process:

父进程:

// In parent.js
    const { fork } = require('child_process');

    const compute = fork('child.js');

    // listen onto the child process
    compute.on('message', (data) => {
        if (!data.finish) {
            compute.send('start');
        } else {
            console.log('Child process finish successfully!')
        }
    });

    // send initial message to start the child process. 
    compute.send('start');