当出现未被程序捕捉的异常时, 整个服务将会终止. 即使有的异常被捕捉, 但是无法返回有效的失败响应, 只能通过返回服务超时也会影响产品的体验. 总的解决方案是 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);
}
});