【问题标题】:How do I prevent node.js from crashing? try-catch doesn't work如何防止 node.js 崩溃?尝试捕捉不起作用
【发布时间】:2011-08-25 08:33:30
【问题描述】:

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

【问题讨论】:

    标签: node.js crash try-catch production-environment


    【解决方案1】:

    PM2

    首先,我强烈建议为Node.js 安装PM2。 PM2 非常擅长处理崩溃和监控 Node 应用程序以及负载平衡。每当它崩溃、因任何原因停止甚至服务器重新启动时,PM2 都会立即启动 Node 应用程序。因此,如果有一天即使在管理我们的代码之后,应用程序崩溃了,PM2 也可以立即重新启动它。欲了解更多信息,Installing and Running PM2

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

    如果有人使用其他声明的答案,请阅读 Node Docs:

    请注意,uncaughtException 是一种非常粗糙的异常处理机制,将来可能会被删除

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

    所以在经历了之后,我终于想出了 Node 文档本身的建议:

    不要使用uncaughtException,而是使用domainscluster。如果您确实使用了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 完成,该模块将被完全弃用。绝对必须拥有域提供的功能的用户可能暂时依赖它,但预计将来必须迁移到不同的解决方案。

    但是在没有引入新的替代品之前,Domain with Cluster 是 Node Documentation 建议的唯一好的解决方案。

    为了深入了解DomainCluster阅读

    https://nodejs.org/api/domain.html#domain_domain(Stability: 0 - Deprecated)

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

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

    Cluster & Domains

    【讨论】:

    • 一句警告,域正在等待弃用:link。来自 Node 文档的建议方法是使用集群:link
    • restart your application after every unhandled exception! 如果 2000 个用户正在使用节点 Web 服务器来流式传输视频,并且 1 个用户出现异常,那么重新启动不会中断所有其他用户?
    • @VikasBansal 是的,这肯定会打断所有用户,这就是为什么使用uncaughtException 并使用DomainCluster 是不好的,如果一个用户遇到异常,那么只有他的线程从集群中删除并为他创建一个新的。而且您也不需要重新启动 Node 服务器。另一方面,如果您确实使用uncaughtException,则每次您的任何用户遇到问题时都必须重新启动服务器。因此,将域与集群一起使用。
    • domain 被完全弃用和删除时我们应该怎么做?
    • 找到这个教程给不懂clusterworkers概念的人:sitepoint.com/…
    【解决方案2】:

    我把这段代码放在我的 require 语句和全局声明下:

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

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

    【讨论】:

    • 请注意:此方法效果很好,但请记住所有 HTTP 响应都需要正确结束。这意味着如果在处理 HTTP 请求时发生未捕获的异常,您仍然必须在 http.ServerResponse 对象上调用 end()。但是,您实现这取决于您。如果您不这样做,请求将挂起,直到浏览器放弃。如果您有足够的这些请求,服务器可能会耗尽内存。
    • @BMiner,你能提供一个更好的实现吗?我注意到了这个问题(请求挂起),所以这并不比使用forever 或其他东西重新启动服务器更好。
    • 这需要深入的解释。我知道这很糟糕,但是每当发生未捕获的异常时,您的服务器都需要尽快重新启动。真的,'uncaughtException'事件的目的是利用它作为发送警告邮件的机会,然后使用process.exit(1);关闭服务器。您可以使用 forever 或类似的东西来重新启动服务器。任何挂起的 HTTP 请求都将超时并失败。你的用户会生你的气。但是,这是最好的解决方案。你为什么问?结帐stackoverflow.com/questions/8114977/…
    • 要从未捕获的错误中获取更多信息,请使用:console.trace(err.stack);
    • 警告:node 的文档毫不含糊地说,你不应该这样做,因为它非常危险:nodejs.org/api/process.html#process_event_uncaughtexception
    【解决方案3】:

    正如提到的here,您会发现error.stack 提供了更完整的错误消息,例如导致错误的行号:

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

    【讨论】:

      【解决方案4】:

      试试supervisor

      npm install supervisor
      supervisor app.js
      

      或者您可以安装forever

      所有这一切都会在您的服务器崩溃时通过重新启动来恢复它。

      forever 可在代码中用于正常恢复任何崩溃的进程。

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

      【讨论】:

      • 这肯定不是解决方案...在服务器关闭期间,它无法响应新的传入请求。应用程序代码可能会抛出异常 - 服务器需要响应 500 错误,而不仅仅是崩溃并希望它重新启动。
      • 所以作为一名黑客,他们可能会发现他们需要向服务器发送一个简单的请求并错过了一个请求参数——这会导致 javascript 中的 undef 导致 node.js 崩溃.有了你的建议,我可以反复杀死你的整个集群。答案是让应用程序优雅地失败——即处理未捕获的异常而不是崩溃。如果服务器正在处理许多 voip 会话怎么办?它的崩溃和燃烧以及所有现有的会话都随着它而死,这是不可接受的。您的用户很快就会离开。
      • @AntKutschera 这就是为什么例外应该是例外情况。只有在您无法恢复并且进程必须崩溃的情况下才会触发异常。您应该使用其他方式来处理这些异常情况。但我明白你的意思。你应该尽可能优雅地失败。然而,在某些情况下,继续损坏状态会造成更大的损害。
      • 是的,这里有不同的思想流派。我学习它的方式(Java 而不是 Javascript)有一些你应该期望的可接受的期望,可能称为业务异常,然后是运行时异常或错误,你不应该期望恢复,比如内存不足。没有优雅失败的一个问题是,我编写的某个库可能会声明它在某些可恢复的情况下抛出异常,比如用户可以在哪里更正他们的输入。在您的应用中,您没有阅读我的文档,只是崩溃,用户可能已经能够恢复
      • @AntKutschera 这就是我们记录异常的原因。您应该分析您的生产日志中的常见异常,并弄清楚您是否以及如何从它们中恢复,而不是让服务器崩溃。我已经在 PHP、Ruby on Rails 和 Node.js 中使用了这种方法。不管你是否退出一个进程,每次你抛出一个 500 错误,你都是在伤害你的用户。这不是 JavaScript 或特定于节点的做法。
      【解决方案5】:

      使用 try-catch 可能会解决未捕获的错误,但在一些复杂的情况下,它并不能正确完成诸如捕获异步函数之类的工作。请记住,在 Node 中,任何异步函数调用都可能包含潜在的应用程序崩溃操作。

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

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

      要确保您的应用在服务器崩溃时也能正常运行,请使用以下步骤:

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

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

      3. 使用 forever/supervisor 来监控您的服务

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

      希望这会有所帮助!

      【讨论】:

        【解决方案6】:

        试试 pm2 节点模块,它非常一致并且有很好的文档。具有内置负载均衡器的 Node.js 应用程序的生产流程管理器。请避免出现 uncaughtException 这个问题。 https://github.com/Unitech/pm2

        【讨论】:

        • `在每个未处理的异常后重新启动您的应用程序!` 如果 2000 个用户正在使用节点 Web 服务器进行流式传输视频,并且 1 个用户遇到异常,那么重新启动不会中断所有其他用户?跨度>
        • 当我发现 PM2.很棒的软件
        【解决方案7】:

        在 restify 上效果很好:

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

        【讨论】:

          【解决方案8】:

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

          https://github.com/vacuumlabs/yacol

          可以帮助您做到这一点。通过一些额外的编写,你可以在你的代码周围拥有很好的领域语义!

          【讨论】:

            【解决方案9】:

            默认情况下,Node.js 通过将堆栈跟踪打印到 stderr 并以代码 1 退出来处理此类异常,覆盖之前设置的任何 process.exitCode。

            know more

            process.on('uncaughtException', (err, origin) => {
                console.log(err);
            });
            

            【讨论】:

              猜你喜欢
              • 2018-03-25
              • 1970-01-01
              • 2016-02-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-01-25
              • 1970-01-01
              相关资源
              最近更新 更多