从 Node.JS 中的未捕获异常中恢复

Posted

技术标签:

【中文标题】从 Node.JS 中的未捕获异常中恢复【英文标题】:Recover from Uncaught Exception in Node.JS 【发布时间】:2011-12-28 05:39:51 【问题描述】:

好的,所以我有一个问题。如果在处理 HTTP 请求时发生未捕获的异常,我没有机会调用 http.ServerResponse 对象的 end() 方法。因此,服务器永远挂起并且永远不会满足请求。

这是一个例子:

var express = require('express');
var app = express.createServer();
var reqNum = 0;
app.get('/favicon.ico', function(req, res) res.send(404););
app.get('*', function(req, res, next) 
    console.log("Request #", ++reqNum, ":", req.url);
    next();
);
app.get('/error', function(req, res, next) 
    throw new Error("Problem occurred");
);
app.get('/hang', function(req, res, next) 
    console.log("In /hang route");
    setTimeout(function() 
        console.log("In /hang callback");
        if(reqNum >= 3)
            throw new Error("Problem occurred");
        res.send("It worked!");
    , 2000);
);
process.on('uncaughtException', function(err) 
    console.log("Uncaught exception!", err);
);
app.listen(8080);

如果你访问/error,就会发生异常,但是会被捕获。用户收到一条错误消息 - 没问题。但是,如果我访问 /hang,服务器最终将抛出未捕获的异常并永远挂起。任何后续的 /hang 请求都将挂起。

这太糟糕了。有关如何解决此问题的任何建议?

【问题讨论】:

【参考方案1】:

如果同步抛出错误,express不会停止工作,只返回500。

this.app.get("/error", (request, response) => 
  throw new Error("shouldn't stop");
);

如果异步抛出错误,express 会崩溃。但是根据它的official documentation,还是有办法恢复的,调用next

this.app.get("/error", (request, response, next) => 
  setTimeout(() => 
    try 
      throw new Error("shouldn't stop");
     catch (err) 
      next(err);
    
  , 0);
);

这将使 express 尽其职责以 500 错误响应。

【讨论】:

【参考方案2】:

使用 try/catch/finally。

app.get('/hang', function(req, res, next) 
    console.log("In /hang route");
    setTimeout(function() 
        console.log("In /hang callback");
        try 
            if(reqNum >= 3)
                throw new Error("Problem occurred");
         catch (err) 
            console.log("There was an error", err);
         finally 
            res.send("It worked!");
        
    , 2000);
);

【讨论】:

丑陋但实用【参考方案3】:

当发生未捕获的异常时,您处于不干净状态。让进程终止并重新启动它,您无法安全地将其恢复到已知良好状态。使用forever,它会在它死后立即重新启动你的进程。

【讨论】:

我在一定程度上同意。在生产环境中,您不能仅仅因为一个未捕获的异常而真正关闭整个服务器。如果单个 php 页面出现问题怎么办?你的整个网络服务器会宕机吗?如果发生未捕获的异常,我将让服务器向我发送电子邮件,如果在 60 秒内发生超过 10 个异常,我将自动关闭。但是,真的......我不希望服务器停机并重新启动。我想尝试恢复。 @BMiner 重新启动您的应用程序比向客户道歉他们的数据损坏更容易。正如 thejh 所说,运行时处于不干净状态。从现在开始,您应该假设一切都是未定义的行为。 @thejh 为什么我必须假设我处于不洁状态?也许只有程序的一部分被破坏了?为什么关闭并重新启动更安全?我相信你们,但我仍然持怀疑态度。你能举个例子吗? @BMiner:你说的是“也许”。问题是,throw 就像 GOTO,当事情完成一半时它会跳出你的代码,现在你的状态可能已经损坏。示例:user.name = newname; db.save(user.id, 'name', newname) - 如果db.save 在完成之前中断怎么办?您的 DB 包含的名称与您在 RAM 中的名称不同。 完全同意@thejh,您应该让进程立即终止(以避免数据损坏等),然后重新启动您的服务器。

以上是关于从 Node.JS 中的未捕获异常中恢复的主要内容,如果未能解决你的问题,请参考以下文章

技术题目收集整理

自己定义Application的未捕获异常处理

无效更新:第 0 节中的无效行数以 NSException 类型的未捕获异常终止

从 Swit2.3 到 Swift3 的转换导致“以 NSException 类型的未捕获异常终止”

合并排序算法中的 C++“以 std::out_of_range:vector 类型的未捕获异常终止”错误

java CrashHandler - 一个类捕获Android App的未捕获异常