Node - 异常捕获

当出现未被程序捕捉的异常时, 整个服务将会终止. 即使有的异常被捕捉, 但是无法返回有效的失败响应, 只能通过返回服务超时也会影响产品的体验. 总的解决方案是 try…catch 捕获程序中的同步异常, 异步异常分为多种情况

  • Promise不需要使用try…catch,直接使用.catch()
  • Generator 可以直接使用co 函数库来使用try…catch
  • async 和 await 可以直接使用try…catch
  • 回调函数可以通过 domain 模块来捕捉异常. (domain 模块正在被弃用, 谨慎使用)
  • process.on(‘uncaughtException’, function(e) { … }) 捕捉未被程序捕捉的异常, 需要注意的是 ‘uncaughtException’ 异常会丢失当前环境的堆栈信息, 导致 Node 不能进行内存回收, 也就是说每一次的 uncaughtException 都有可能导致内存泄漏, 为了防止内存泄漏, 需要优雅的退出程序.
  • 对比之下 process.on(‘unhandledRejection’, function(e){ … }), 捕捉的是未被 catch 的 promise 内的异常, 也就是 promise 内的 reject. 不同的是 unhandledRejection 并不会直接使程序退出, 只是会抛出 UnhandledPromiseRejectionWarning: …

因为 domain 即将被弃用, 就不重点研究了, 感兴趣的可以看一下相关的参考文章: https://nodejs.org/api/domain.html# https://cnodejs.org/topic/516b64596d38277306407936

# uncaughtException

为了防止 uncaughtException 导致内存泄漏, 当捕捉到 uncaughtException 时, 应该重启服务.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// uncaughtException 避免程序崩溃
process.on('uncaughtException', function (err) {
    console.log(err);

    try {
        // 强制退出机制 
        var timer = setTimeout(function () {
            process.exit(1);
        }, 30000);
        timer.unref();
        
        // 自动退出机制
        server.close();
    } catch (e) {
        console.log('error when exit', e.stack);
    }
});
  • Node 所有连接都被释放后进程会自动结束, 所以不需要在 server.close 方法的回调函数中退出进程
  • 强制退出机制, 用户连接有可能因为某些原因无法释放,在这种情况下应该强制退出整个进程。
  • timer.unref(): 如果不使用 unref 方法,那么即使 server 的所有连接都关闭,Node 也会保持运行直到 timer 的回调函数被调用。unref 可以创建一个"不保持程序运行"的计时器。
  • 处理异常时要小心的把异常处理逻辑用 try/catch 包住,避免处理异常时抛出新的异常

# 和 cluster 一起使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  var cluster = require('cluster');

  process.on('uncaughtException', function (err) {
      console.log(err);
  
      try {
          var timer = setTimeout(function () {
              process.exit(1);
          }, 30000);
          timer.unref();
  
          server.close();
  
          if (cluster.worker) {
              cluster.worker.disconnect();
          }
      } catch (e) {
          console.log('error when exit', e.stack);
      }
  });
Licensed under CC BY-NC-SA 4.0