异样解决是程序运行中必须要关注的中央,当异样呈现后,应该第一工夫关注到,并且疾速解决。大部分程序员们都不敢保障本人的代码百分比正确,所以应该在写代码时就要对异样提前做预防解决,尽量保障在异样呈现时,给用户一个敌对的提醒,不至于服务挂起导致申请超时,并且能将异样信息做记录上报,不便前期排查解决。

一. 同步代码的异样捕捉解决

  1. 同步代码中的异样应用try{}catch构造即可捕捉解决。

    try {  throw new Error('错误信息');} catch (e) {  console.error(e.message);}

    能够失常捕捉到。

二. 异步代码的错误处理

  1. try/catch 接口

异步代码下应用try{}catch构造捕捉解决成果如何呢?

try {  setTimeout(()=>{    throw new Error('错误信息');  })} catch (e) {  console.error('error is:', e.message);}

然而却没有捕捉到异步谬误。

  1. process的uncaughtException事件

那异步谬误该怎么解决呢?首先换个思维,因为异样并不是当时筹备好的,不能管制其到底在哪儿产生,所以站更高的角度,如监听利用过程的谬误异样,从而捕捉不能意料的谬误异样,保障利用不至于奔溃调。

process.on('uncaughtException', (e)=>{  console.error('process error is:', e.message);});

如上代码从process上监听uncaughtException事件,能够捕捉到整个过程蕴含异步中的错误信息,从而保障利用没有奔溃。

然而新的问题随之而来,因为异样不可意料的产生后,当异样呈现时,间接从对应执行栈中断,而到process捕捉的异样事件下,导致了v8引擎的垃圾回收性能不能依照失常流程工作,而后开始呈现内存透露问题。

绝对于异样来说,内存透露也是一个不能漠视的重大问题,而process.on('uncaughtException')的做法,很难去保障不造成内存的透露。所以当捕捉到异样时,显式的手动杀掉过程,并开始重启node过程,即保障开释内存,又保障了保障服务后续失常可用。

process.on('uncaughtException', (e)=>{  console.error('process error is:', e.message);  process.exit(1);  restartServer(); // 重启服务});

然而下面的做法有一点间接,大家未免存纳闷,如果单过程单实例的部署下,杀掉过程在重启这一段时间内服务不能失常可用怎么办?这显然是不合理的。

  1. 应用domain模块
    domain模块,把解决多个不同的IO的操作作为一个组。注册事件和回调到domain,当产生一个谬误事件或抛出一个谬误时,domain对象会被告诉,不会失落上下文环境,也不导致程序谬误立刻退出,与process.on('uncaughtException')不同。

Domain 模块可分为隐式绑定和显式绑定:
隐式绑定: 把在domain上下文中定义的变量,主动绑定到domain对象
显式绑定: 把不是在domain上下文中定义的变量,以代码的形式绑定到domain对象

const domain = require('domain');const d = domain.create();d.on('error', (err) => {  console.log('err', err.message);  console.log(needSend.message);});const needSend = { message: '须要传递给错误处理的一些信息' };d.add(needSend);function excute() {  try {    setTimeout(()=>{      throw new Error('错误信息');    });  } catch (e) {    console.error('error is:', e.message);  }};d.run(excute);

domin显著的长处,能把出问题时的一些信息传递给谬误处理函数,能够做一些打点上报等解决工作,最起码保障重启后的服务,程序猿们晓得产生了什么,有线索可查,也能够抉择传递上下文进去,做一些后续解决。比方当服务出错的时候,能够把用户申请栈信息传给上游,返回告知用户服务异样,而不是用户始终等到申请主动超时。

...d.add(res);...d.on('error', (err) => {  console.log('err', err.message);  res.end('服务器产生异样,请稍后再试!');});

然而它和process.on('uncaughtException')的做法一样,很难去保障不造成内存的透露。

另外在官网文档上,domain模块解决废除状态,然而当初也没有其余计划能够齐全代替domain模块,然而我当初node10的版本仍旧能够用,临时应该不必放心domain模块被废除的问题。

三. 多过程模式加异样捕捉后重启
下面的形式没有完满解决问题,思考一下如何可能让异样产生后不奔溃,捕捉异样后不造成内存透露,而且重启开释缓存不造成服务不可用呢?

一种比拟好的计划是,以多过程(cluster)的模式去部署利用,当某一个过程被异样捕捉后,能够做一下打点上报后,开始重启开释内存,此时其余申请被承受后,其余过程仍旧能够对外提供服务,当然前提是你的利用不能异样多的数都数不清。

上面是将cluster和domain联合起来应用,以多过程的形式保障服务可用,同时能够将错误信息传递上来进行上报,并且保留谬误呈现的上下文环境,给用户返回申请,不让用户申请超时,而后在手动杀死异样过程,而后重启。

const cluster = require('cluster');const os = require('os');const http = require('http');const domain = require('domain');const d = domain.create();if (cluster.isMaster) {  const cpuNum = os.cpus().length;  for (let i = 0; i < cpuNum; ++i) {    cluster.fork()  };  // fork work log  cluster.on('fork', worker=>{    console.info(`${new Date()} worker${worker.process.pid}过程启动胜利`);  });  // 监听异样退出过程,并从新fork  cluster.on('exit',(worker,code,signal)=>{    console.info(`${new Date()} worker${worker.process.pid}过程启动异样退出`);    cluster.fork();  })} else {  http.createServer((req, res)=>{    d.add(res);    d.on('error', (err) => {      console.log('记录的err信息', err.message);      console.log('出错的 work id:', process.pid);      // uploadError(err)  // 上报错误信息至监控      res.end('服务器异样, 请稍后再试');      // 将异样子过程杀死      cluster.worker.kill(process.pid);    });    d.run(handle.bind(null, req, res));  }).listen(8080);}function handle(req, res) {  if (process.pid % 2 === 0) {    throw new Error(`出错了`);  }  res.end(`response by worker: ${process.pid}`);};

有趣味的同学强烈建议把代码拷贝到本地后本人调试察看。

关键词:前端培训