异常处理是我们程序开发中的一个最常用的功能,系统响应中断,执行事先编写好的异常处理代码, 进行控制转移
处理异常往往是我们担心程序执行过程中出现问题, 从而引入的一种快速响应的机制, 它可以让我们的程序变得更健壮, 但是如果不能正确的处理异常, 可能会起到相反的效果,
接下来我们可以试着分析一下, 下面的异常处理方法有哪些问题?
function(){
if (doCheck(){ // 检测
throw BusinessException.ofMessage(code, "business serivce error, " + object.getStatus());
}
try {
doSomething(); // 业务逻辑
} catch (Exception e) {
log.error("business serivce request: {}, error:{}", JSON.toString(request), e.getMessage());
}
}
这段代码我归结了4处不合理的地方
- 异常无感知
我们捕获异常的地方, 只是简单的做了一个log.error, 将异常打印到了本地的日志文件中, 并没有通知我们, 那么当异常发生的时候,我们是无感的
不暴露异常比抛出异常更可怕
这会造成非常严重的后果, 使我们发现问题的时间远远大于问题发生的时间。那么应该怎么做呢? 我们需要在异常发生的地方做打点/通知, 一般公司都会有自己的日志收集器,我们可以配置一些规则,比如超过某一阈值后报警, 保证为我们可以第一时间感知问题的发生,从而去解决问题
- 异常处理简陋
我们在check的时候, 如果发生异常, 会直接throw出去, 如果外层没有try…catch住这个异常, 这个异常就会被框架所捕获, 从而影响服务可用性, 这种异常处理是非常粗暴的, 业务异常不应该影响服务的可用性, 所以这里需要检测上层是否有异常捕获, 这里有一原则
如果知道如何处理, 则进行功能处理, 不知道如何处理, 可以收口到上游统一处理
-
异常信息无意义
假设doCheck出现问题, 这个时候用户可以看到提示是 “business serivce error, 2”, 这种提示非常不友好的, 他并没有实际的含义, 所以为了方便定位问题,我们抛出的异常,尤其是用户可见的异常,一定要是中文且有意义的;如果有能力,应该提示使用者下一步需要如何去处理 -
吞异常栈
这里主要指的是e.getMessage(), 这个方法不会返回堆栈信息, 我们处理异常的是error方法的最后一个参数, 一定要是Throwable类型的, 这样才能打印出完整的异常栈
最后我们将上述的代码重写一遍, 结果如下
function(){
if (doCheck(){ // 检测, 上层提供统一的异常逻辑
throw BusinessException.ofMessage(code, "服务状态异常, 请联系开发人员, " + object.getStatus()); // 业务异常
}
try {
doSomething(); // 业务逻辑
} catch (Exception e) {
Perf("do.function.fail"); // 打点, 超过阈值报警
log.error("business serivce request: {}", JSON.toString(request), e); // 打印完整的堆栈
throw BusinessException.ofMessage(code, "业务操作失败, " + object.traceId()); // 业务异常, 提供标识助于排查问题
}
}
以上是处理异常代码的一些心得,欢迎交流,感谢阅读