共计 3261 个字符,预计需要花费 9 分钟才能阅读完成。
异样解决是程序运行中必须要关注的中央,当异样呈现后,应该第一工夫关注到,并且疾速解决。大部分程序员们都不敢保障本人的代码百分比正确,所以应该在写代码时就要对异样提前做预防解决,尽量保障在异样呈现时,给用户一个敌对的提醒,不至于服务挂起导致申请超时,并且能将异样信息做记录上报,不便前期排查解决。
一. 同步代码的异样捕捉解决
-
同步代码中的异样应用 try{}catch 构造即可捕捉解决。
try {throw new Error('错误信息'); } catch (e) {console.error(e.message); }
能够失常捕捉到。
二. 异步代码的错误处理
- try/catch 接口
异步代码下应用 try{}catch 构造捕捉解决成果如何呢?
try {setTimeout(()=>{throw new Error('错误信息');
})
} catch (e) {console.error('error is:', e.message);
}
然而却没有捕捉到异步谬误。
- 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(); // 重启服务});
然而下面的做法有一点间接,大家未免存纳闷,如果单过程单实例的部署下,杀掉过程在重启这一段时间内服务不能失常可用怎么办?这显然是不合理的。
- 应用 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}`);
};
有趣味的同学强烈建议把代码拷贝到本地后本人调试察看。
关键词: 前端培训