使node.js不会在出错时退出

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

我特别关心的不是这个bug,而是当这样的错误发生时,整个服务器都closures了。 有没有什么我可以在节点的全局层面上做,所以如果发生错误,它只会logging一条消息,也许杀死事件,但服务器进程将继续运行?

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

您可以将侦听器附加到进程对象的`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.'); 

所有你现在要做的就是logging它或者做些什么,以防万一你知道在什么情况下发生了错误,你应该在Socket.IO的GitHub页面上提交一个bug:
https://github.com/LearnBoost/Socket.IO-node/issues

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

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

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

警告:正确使用“uncaughtException”

请注意,'uncaughtException'是一个粗暴的exception处理机制,意图仅用作最后的手段。 该事件不应等同于“错误继续下一步”。 未处理的exception本质上意味着应用程序处于未定义的状态。 尝试恢复应用程序代码而没有正确地从exception恢复可能会导致额外的无法预料的和不可预知的问题。

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

试图正常恢复后,未捕获的exception可能类似于拔出电源线时升级一台计算机 – 十次中有九次没有任何反应 – 但第10次,系统损坏。

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

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

我只是做了一大堆研究(见这里 , 这里 , 这里 , 这里 ),你的问题的答案是,节点不会允许你写一个error handling程序,将捕获可能发生在您的每个错误情况系统。

像express这样的框架将允许你捕捉某些types的错误(当一个asynchronous方法返回一个错误对象时),但是还有其他的条件你不能用全局error handling器来捕捉。 这是Node的限制(在我看来),也可能是一般的asynchronous编程。

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

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

假设文件“some / file”实际上不存在。 在这种情况下,fs.readFile将返回一个错误作为callback方法的第一个参数。 如果你检查并做下一步(错误)时,默认的error handling程序将接pipe并做任何你做它(例如返回500的用户)。 这是一个处理错误的优雅方式。 当然,如果你忘了next(err)电话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"); } }); }); 

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

Node中目前没有全局error handling函数来处理这种情况。 你不能在你所有的快速处理程序中放一个巨大的try/catch ,因为当你的asyncallback执行的时候,那些try/catch块不在范围之内。 这只是asynchronous代码的本质,它打破了try / catcherror handling范例。

AFAIK,你唯一的办法就是把try/catch块放到你的代码的同步部分,在你的每个asynchronouscallback里面,像这样:

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

这将使一些讨厌的代码,特别是一旦你开始进入嵌套的asynchronous调用。

有些人认为,在这些情况下(即死亡),Node做的是正确的事情,因为你的系统处于不一致的状态,你没有别的select。 我不同意这种推理,但我不会就此进行哲学辩论。 关键是,使用Node,你的select是很多try/catch块,或者希望你的testing覆盖率足够好,这样就不会发生。 你可以把新手或主pipe这样的东西放在适当的位置,以便在应用程序崩溃时重新启动应用程序,但这只是缓解问题,而不是解决scheme。

Node.js有一个目前不稳定的function,称为域似乎解决这个问题,但我不太了解它。

我只是把一个监听未处理exception的类放在一起,当它看到一个类时:

  • 将堆栈跟踪打印到控制台
  • 将其logging在自己的日志文件中
  • 通过电子邮件发送堆栈跟踪
  • 重新启动服务器(或杀死它,由你决定)

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

一探究竟!

注意:现在已经有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... ); } ); 

有类似的问题。 伊沃的答案是好的。 但是,如何在循环中捕获错误并继续?

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

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

我试着直接添加一个处理程序:

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

这也没有工作。

在可疑的fs.statSynch()周围添加try / catch是我的最佳解决scheme:

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

然后导致代码修复(从文件夹和文件中创build一个干净的pathvariables)。

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

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

在发生错误的subprocess中,用'uncaughtException'来捕获,以避免应用程序崩溃。 请注意,从事件处理程序中抛出的exception不会被捕获 。 一旦错误被安全地发现,发送如下消息: {finish:false}

Parent Process将监听消息事件,并再次将消息发送给subprocess以重新运行该function。

儿童程序:

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

父过程:

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