如何防止node.js崩溃?尝试捕获不起作用


157

根据我的经验,php服务器会向日志或服务器端抛出异常,但是node.js只会崩溃。因为尝试都是异步完成的,所以用try-catch包围我的代码也不起作用。我想知道其他人在生产服务器中做什么。

Answers:


132

您可以从Node自己的文档中(http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception)阅读其他答案,真是太疯狂了

如果有人在使用其他规定的答案,请阅读Node Docs:

请注意,这uncaughtException是异常处理的非常粗糙的机制,以后可能会删除

下午2

首先,我会强烈建议安装PM2Node.js。PM2非常适合处理崩溃和监视Node应用程序以及负载平衡。每当PM2崩溃时,PM2都会立即启动Node应用程序,无论出于任何原因甚至在服务器重新启动时都会停止。因此,即使有一天即使在管理完我们的代码之后,应用程序崩溃,PM2仍可以立即重新启动它。有关更多信息,请安装和运行PM2

现在回到我们的解决方案,以防止应用程序本身崩溃。

因此,经过研究之后,我终于想到了Node文档本身的建议:

不要使用uncaughtException,而是使用domainswith cluster。如果您使用uncaughtException,请在每个未处理的异常后重新启动应用程序!

DOMAIN丛集

我们实际上要做的是向触发错误的请求发送错误响应,同时让其他请求在正常时间内完成,并停止侦听该工作线程中的新请求。

这样,域使用与群集模块紧密结合,因为当工作进程遇到错误时,主进程可以派生新的工作进程。请参阅下面的代码以了解我的意思

通过使用Domain,以及使用Cluster可以将程序分为多个工作进程的弹性,我们可以做出更适当的反应,并以更高的安全性处理错误。

var cluster = require('cluster');
var PORT = +process.env.PORT || 1337;

if(cluster.isMaster) 
{
   cluster.fork();
   cluster.fork();

   cluster.on('disconnect', function(worker) 
   {
       console.error('disconnect!');
       cluster.fork();
   });
} 
else 
{
    var domain = require('domain');
    var server = require('http').createServer(function(req, res) 
    {
        var d = domain.create();
        d.on('error', function(er) 
        {
            //something unexpected occurred
            console.error('error', er.stack);
            try 
            {
               //make sure we close down within 30 seconds
               var killtimer = setTimeout(function() 
               {
                   process.exit(1);
               }, 30000);
               // But don't keep the process open just for that!
               killtimer.unref();
               //stop taking new requests.
               server.close();
               //Let the master know we're dead.  This will trigger a
               //'disconnect' in the cluster master, and then it will fork
               //a new worker.
               cluster.worker.disconnect();

               //send an error to the request that triggered the problem
               res.statusCode = 500;
               res.setHeader('content-type', 'text/plain');
               res.end('Oops, there was a problem!\n');
           } 
           catch (er2) 
           {
              //oh well, not much we can do at this point.
              console.error('Error sending 500!', er2.stack);
           }
       });
    //Because req and res were created before this domain existed,
    //we need to explicitly add them.
    d.add(req);
    d.add(res);
    //Now run the handler function in the domain.
    d.run(function() 
    {
        //You'd put your fancy application logic here.
        handleRequest(req, res);
    });
  });
  server.listen(PORT);
} 

尽管Domain正在等待弃用,但随着新的替换如Node文档中所述,将被删除

该模块即将弃用。替代API最终确定后,该模块将被完全弃用。绝对必须具有域提供的功能的用户暂时可能会依赖它,但是应该期望将来必须迁移到其他解决方案。

但是直到没有引入新的替代方案之前,“带群集的域”是Node Documentation建议的唯一好的解决方案。

供深入了解DomainCluster阅读

https://nodejs.org/api/domain.html#domain_domainStability: 0 - Deprecated

https://nodejs.org/api/cluster.html

感谢@Stanley Luo向我们分享了有关集群和域的精彩深入解释

集群与域


9
一个警告,Domain正在等待弃用:link。来自Node docs的建议方法是使用cluster:link
保罗

4
restart your application after every unhandled exception!如果2000个用户使用节点Web服务器来流式传输视频,而1个用户遇到异常,那么重新启动不会中断所有其他用户吗?
维卡斯·班萨尔

2
@VikasBansal是的,这肯定会打断所有用户,这就是为什么不好uncaughtExceptionDomainCluster而是与之一起使用的原因,因此,如果一个用户遇到异常,那么只有他的线程从群集中删除并为他创建了一个新线程。而且您也不需要重启节点服务器。另一方面,如果您确实使用uncaughtException过,则每次任何用户遇到问题时都必须重新启动服务器。因此,将Domain与Cluster一起使用。
2016年

3
我们该怎么办时,domain完全否定并取消?
2016年

3
发现这个教程对于那些谁不明白的概念clusterworkerssitepoint.com/...
斯坦利罗

81

我将此代码放在我的require语句和全局声明下:

process.on('uncaughtException', function (err) {
  console.error(err);
  console.log("Node NOT Exiting...");
});

为我工作。我唯一不喜欢的是,如果我让事情崩溃,我会得到的信息不多。


45
请注意:此方法很好用,但请记住,所有HTTP响应都必须正确结束。这意味着,如果在处理HTTP请求时发生未捕获的异常,则仍必须在http.ServerResponse对象上调用end()。但是,您可以实现这取决于您。如果您不这样做,该请求将一直挂起,直到浏览器放弃为止。如果您有足够的这些请求,服务器可能会耗尽内存。
BMiner

3
@BMiner,您能提供更好的实现吗?我注意到了这个问题(请求挂起),所以这实际上并不比仅使用forever或重新启动服务器更好。
pixelfreak 2012年

6
这需要深入的解释。我知道这很糟糕,但是无论何时发生未捕获的异常,您的服务器都需要尽快重新启动。实际上,“ uncaughtException”事件的目的是利用它作为发送警告电子邮件的机会,然后使用process.exit(1);。关闭服务器。您可以永久使用或类似方法重新启动服务器。任何未决的HTTP请求都将超时并失败。您的用户会生您的气。但是,这是最好的解决方案。你为什么问?结帐stackoverflow.com/questions/8114977/...
BMiner

3
要从未捕获的错误中获取更多信息,请使用:console.trace(err.stack);
Jesse Dunlap 2013年

2
警告:node的文档明确地说,绝对不要这样做,因为它非常危险: nodejs.org/api/process.html#process_event_uncaughtexception
Jeremy Logan

28

如此处所述您将找到error.stack更完整的错误消息,例如引起错误的行号:

process.on('uncaughtException', function (error) {
   console.log(error.stack);
});

12

尝试 supervisor

npm install supervisor
supervisor app.js

或者,您也可以安装forever

这一切将是通过重新启动服务器来使其崩溃时恢复服务器。

forever 可以在代码内使用,以正常恢复崩溃的任何进程。

forever文档以编程方式提供了有关退出/错误处理的可靠信息。


9
当然,这不是解决方案……在服务器关闭期间,它无法响应新的传入请求。应用程序代码可能会引发异常-服务器需要响应500错误,而不仅仅是崩溃并希望它重新启动。
Ant Kutschera

20
因此,作为一名黑客,人们可能会发现他们需要向服务器发送一个简单的请求,而错过了一个请求参数-这导致javascript中的undef导致node.js崩溃。根据您的建议,我可以反复杀死您的整个集群。答案是使应用程序正常运行-即处理未捕获的异常而不会崩溃。如果服务器正在处理许多voip会话,该怎么办?它无法崩溃和刻录,以及所有现有会话都将因此消失而无法接受。您的用户很快就会离开。
Ant Kutschera

5
@AntKutschera,这就是为什么异常应该是例外情况的原因。仅在无法恢复并且进程必须崩溃的情况下才会引发异常。您应该使用其他方法来处理这些例外情况。但我明白你的意思。您应该尽可能地优雅地失败。但是,在某些情况下继续处于损坏状态会造成更大的损失。
雷诺斯

2
是的,这里有不同的思想流派。我学习它的方式(用Java而不是Javascript)有一些您应该期望的可接受的期望,称为业务异常,然后有一些运行时异常或错误,您不应指望它们会恢复,例如内存不足。不能正常失败的一个问题是,我编写的某些库可能声明在某些可恢复的情况下抛出异常,例如用户可以更正其输入。在您的应用程序中,您没有阅读我的文档,而只是崩溃了,用户可能已经可以在其中恢复
Ant Kutschera

1
@AntKutschera这就是我们记录异常的原因。您应该分析生产日志中的常见异常,并确定是否以及如何从中恢复,而不是使服务器崩溃。我在PHP,Ruby on Rails和Node中使用了该方法。无论您是否退出某个进程,每次抛出500错误时,都会给用户带来伤害。这不是JavaScript或特定于Node的实践。
Eric Elliott

7

使用try-catch可能会解决未捕获的错误,但是在某些复杂的情况下,它将无法正确执行捕获异步功能之类的工作。请记住,在Node中,任何异步函数调用都可能包含潜在的应用崩溃操作。

使用uncaughtException是一种解决方法,但是它被认为是低效的,并且可能在Node的未来版本中被删除,因此不要指望它。

理想的解决方案是使用域:http : //nodejs.org/api/domain.html

为确保您的应用程序启动并运行,甚至服务器崩溃,请使用以下步骤:

  1. 使用节点群集为每个核心分叉多个进程。因此,如果一个进程死亡,则另一个进程将自动启动。检出:http : //nodejs.org/api/cluster.html

  2. 使用域来捕获异步操作,而不是使用try-catch或未捕获。我并不是说尝试捕获或未捕获是不好的想法!

  3. 永远使用/主管来监视您的服务

  4. 添加守护程序以运行您的节点应用程序:http : //upstart.ubuntu.com

希望这可以帮助!


4

尝试一下pm2节点模块,它是非常一致的,并且文档丰富。带有内置负载均衡器的Node.js应用程序的生产过程管理器。请避免针对此问题的uncaughtException。 https://github.com/Unitech/pm2


`每个未处理的异常后重新启动应用程序!`如果2000个用户使用节点Web服务器来流式传输视频,而1个用户遇到异常,那么重新启动不会中断所有其他用户吗?
Vikas Bansal

当我发现PM2时,我感到非常高兴。伟大的软件
Mladen Janjetovic

0

UncaughtException是一种“非常粗糙的机制”(是如此),现在不推荐使用域。但是,我们仍然需要某种机制来捕获(逻辑)域周围的错误。图书馆:

https://github.com/vacuumlabs/yacol

可以帮助您做到这一点。只需编写一些额外的代码,您就可以在代码周围拥有不错的域语义!


0

在refify上效果很好:

server.on('uncaughtException', function (req, res, route, err) {
  log.info('******* Begin Error *******\n%s\n*******\n%s\n******* End Error *******', route, err.stack);
  if (!res.headersSent) {
    return res.send(500, {ok: false});
  }
  res.write('\n');
  res.end();
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.