前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nodejs中的异常错误处理

nodejs中的异常错误处理

作者头像
用户1462769
发布2019-08-12 20:05:29
2.5K0
发布2019-08-12 20:05:29
举报
文章被收录于专栏:全栈者

异常处理是程序运行中必须要关注的地方,当异常出现后,应该第一时间关注到,并且快速解决。大部分程序员们都不敢保证自己的代码百分比正确,所以应该在写代码时就要对异常提前做预防处理,尽量保证在异常出现时,给用户一个友好的提示,不至于服务挂起导致请求超时,并且能将异常信息做记录上报,方便后期排查解决。

一. 同步代码的异常捕获处理

1. 同步代码中的异常使用try{}catch结构即可捕获处理。

代码语言:javascript
复制
代码语言:javascript
复制
try {
  throw new Error('错误信息');
} catch (e) {
  console.error(e.message);
}

可以正常捕获到。

二. 异步代码的错误处理

1. try/catch 接口

异步代码下使用try{}catch结构捕获处理效果如何呢?

代码语言:javascript
复制
try {
  setTimeout(()=>{
    throw new Error('错误信息');
  })
} catch (e) {
  console.error('error is:', e.message);
}

执行结果:

然而却没有捕获到异步错误。

2. process的uncaughtException事件

那异步错误该怎么处理呢?首先换个思维,因为异常并不是事先准备好的,不能控制其到底在哪儿发生,所以站更高的角度,如监听应用进程的错误异常,从而捕获不能预料的错误异常,保证应用不至于奔溃调。

代码语言:javascript
复制
代码语言:javascript
复制
process.on('uncaughtException', (e)=>{
  console.error('process error is:', e.message);
});

如上代码从process上监听uncaughtException事件,可以捕获到整个进程包含异步中的错误信息,从而保证应用没有奔溃。

但是新的问题随之而来,因为异常不可预料的发生后,当异常出现时,直接从对应执行栈中断,而到process捕获的异常事件下,导致了v8引擎的垃圾回收功能不能按照正常流程工作,然后开始出现内存泄漏问题。

相对于异常来说,内存泄漏也是一个不能忽视的严重问题,而process.on('uncaughtException')的做法,很难去保证不造成内存的泄漏。所以当捕获到异常时,显式的手动杀掉进程,并开始重启node进程,即保证释放内存,又保证了保证服务后续正常可用。

代码语言:javascript
复制
process.on('uncaughtException', (e)=>{
  console.error('process error is:', e.message);
  process.exit(1);
  restartServer(); // 重启服务
});

但是上面的做法有一点直接,大家不免存疑惑,如果单进程单实例的部署下,杀掉进程在重启这一段时间内服务不能正常可用怎么办?这显然是不合理的。

3. 使用domain模块

domain模块,把处理多个不同的IO的操作作为一个组。注册事件和回调到domain,当发生一个错误事件或抛出一个错误时,domain对象会被通知,不会丢失上下文环境,也不导致程序错误立即退出,与process.on('uncaughtException')不同。

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

代码语言:javascript
复制

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明显的优点,能把出问题时的一些信息传递给错误处理函数,可以做一些打点上报等处理工作,最起码保证重启后的服务,程序猿们知道发生了什么,有线索可查,也可以选择传递上下文进去,做一些后续处理。比如当服务出错的时候,可以把用户请求栈信息传给下游,返回告知用户服务异常,而不是用户一直等到请求自动超时。

代码语言:javascript
复制
...
d.add(res);
...
d.on('error', (err) => {
  console.log('err', err.message);
  res.end('服务器发生异常,请稍后再试!');
});

但是它和process.on('uncaughtException')的做法一样,很难去保证不造成内存的泄漏。

另外在官方文档上,domain模块处理废弃状态,但是现在也没有其他方案可以完全代替domain模块,但是我现在node10的版本依旧可以用,暂时应该不用担心domain模块被废弃的问题。

三. 多进程模式加异常捕获后重启

上面的方式没有完美解决问题,思考一下如何能够让异常发生后不奔溃,捕获异常后不造成内存泄漏,而且重启释放缓存不造成服务不可用呢?

一种比较好的方案是,以多进程(cluster)的模式去部署应用,当某一个进程被异常捕获后,可以做一下打点上报后,开始重启释放内存,此时其他请求被接受后,其他进程依旧可以对外提供服务,当然前提是你的应用不能异常多的数都数不清。

下面是将cluster和domain结合起来使用,以多进程的方式保证服务可用,同时可以将错误信息传递下去进行上报,并且保留错误出现的上下文环境,给用户返回请求,不让用户请求超时,然后在手动杀死异常进程,然后重启。

代码语言:javascript
复制
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}`);
};

有兴趣的同学强烈建议把代码拷贝到本地后自己调试观察。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 同步代码的异常捕获处理
  • 二. 异步代码的错误处理
  • 2. process的uncaughtException事件
  • 3. 使用domain模块
  • 三. 多进程模式加异常捕获后重启
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档