文章目录
  1. 1. nodejs 异步异常处理
    1. 1.1. 怎么解决
      1. 1.1.1. uncaughtException事件监听
      2. 1.1.2. 使用domain模块捕捉异常
    2. 1.2. 使用pm2来解决程序异步异常自动退出

nodejs 异步异常处理

环境:

nodejs : 4.4.4
express :4.1.13

pm2 :1.1.3

nodejs到目前为止已经发行的稳定版本是4.4.4,而用nodejs开发的系统的稳健性一直是被人所诟病的,现在我项目中就出现一有些运行错误或者异常,就导致了整个服务器退出的情况。

那nodejs可不可以像java一样,有一个统一的异常处理的handller,把所有的没有业务处理的异常抛出给统一的ExceptionHandler去处理呢?

注意,这里要处理的都是异步的业务异常,同步的异常处理不在这里讨论

同步异常处理:

//处理所有同步异常
app.use(function (err, req, res, next) {
// 带有四个参数的 middleware 专门用来处理异常
res.json("{'code':10000,'msg':'error'}");
});

怎么解决

目前其实还没有完美的方法去解决这个问题,但总是有替代的或者相似的解决方案:

  • uncaughtException事件监听
  • 使用domain模块捕捉异常

uncaughtException事件监听

uncaughtException 是一个非常古老的事件。当 Node 发现一个未捕获的异常时,会触发这个事件。并且如果这个事件存在回调函数,Node 就不会强制结束进程。

一般情况下,我们会将有可能出错的代码放到try/catch块里。但是到了Node.js,由于try/catch无法捕捉异步回调里的异常,Node.js原生提供uncaughtException事件挂到process对象上,用于捕获所有未处理的异常,以下是一个模似一个异步异常:

process.on('uncaughtException', function(err) {
console.error('Error caught in uncaughtException event:', err);
});

try {
process.nextTick(function() {
    fs.readFile('non_existent.js', function(err, str) {
        if(err) throw err;
        else console.log(str);
    });
});
} catch(e) {
console.error('Error caught by catch block:', e);
}

执行的结果是代码进到了uncaughtException的回调里而不是catch块。 uncaughtException虽然能够捕获异常,但是此时错误的上下文已经丢失,即使看到错误也不知道哪儿报的错,定位问题非常的不利。而且一旦uncaughtException事件触发,而且我们没有自定义重写process的uncaughtException挂载的话,整个node进程将crash掉。

NodeJS对于未捕获异常的默认处理是: - 触发 uncaughtException 事件 - 如果 uncaughtException 没有被监听,那么 - 打印异常的堆栈信息 - 触发进程的 exit 事件;

在app.js里监听了uncaughtException方法的话,进程是不会退出的,但是后面内存会一直飙升,比如,我请求了一个接口,里面模似了异步异常抛出:

app.get('/', function (req, res) {
 setTimeout(function () {
     throw new Error('async exception'); // 抛出一个异步异常
 }, 1000);

});

执行完了以后,就会触发:uncaughtException方法,但不会立马退出,用pm2监控了他的内存使用情况,从19M到 50.309 MB的内存占用,估计随着时间的增加,内存会一直攀升。

image

使用domain模块捕捉异常

使用pm2来解决程序异步异常自动退出

当有异步异常导致服务进程自动退出时,pm2会自动重启应用程序,我请求了两次有异步异常的接口(会导致程序退出),每请求一次,完了后,在终端执行:pm2 show testExpress,得到如下结果:

image

这是为什么?不要问我为什么,这是因为pm2 0.7.1 开始,对因为异步异常触发uncaughtException方法导致的程序退出作了优化,只要进程退出,就会通过pm2自动重启当前程序,如果你用了nodejs的cluster自带的负载均衡,则会重启当前分配的服务。

所以,正确的对uncaughtException的处理是:

process.on('uncaughtException', function(err) {

try {
    //logger writer with err
    var killTimer = setTimeout(function () {
        process.exit(1);
    }, 30000);
    killTimer.unref();

    server.close();

    // if (cluster.worker) {
    //     cluster.worker.disconnect();
    // }
} catch (e) {
    console.log('error when exit', e.stack);
}
});

那配合pm2一起使用,绝对神作。。。。

对于一般的异步异常,不会导致程序退出的业务异常,则用domain来解决:

比如这个异步异常:

app.get('/', function (req, res) {
    // if(!req.params.name)
    //     throw new Error('sync exception'); // 抛出一个同步异常
    //
    setTimeout(function () {
        throw new Error('async exception'); // 抛出一个异步异常
    }, 1000);

});

这个自定义的error异常,不会导致程序退出,也就不会调用uncaughtException方法。

// 使用 domain 来捕获大部分异常
app.use(function (req, res, next) {
    var reqDomain = domain.create();
    reqDomain.on('error', function (err) { // 下面抛出的异常在这里被捕获,只有异步异常
        res.send("{'code':10000,'msg':'error'}"); // 成功给用户返回了 500
        try {
            // 强制退出机制
            var killTimer = setTimeout(function () {
                process.exit(1);
            }, 30000);
            killTimer.unref(); // 非常重要
        
            // 自动退出机制,停止接收新链接,等待当前已建立连接的关闭
            server.close(function () {
                // 此时所有连接均已关闭,此时 Node 会自动退出,不需要再调用
                process.exit(1); //来结束进程
            });
        } catch(e) {
            console.log('err', e.stack);
        }

    });

    reqDomain.run(next);

});
文章目录
  1. 1. nodejs 异步异常处理
    1. 1.1. 怎么解决
      1. 1.1.1. uncaughtException事件监听
      2. 1.1.2. 使用domain模块捕捉异常
    2. 1.2. 使用pm2来解决程序异步异常自动退出