『互联网架构』软件架构-java日志异常(18)
原创文章,欢迎转载。转载请注明:转载自IT人故事会,谢谢!
原文链接地址:『互联网架构』软件架构-java日志异常(18)
上次说了日志,不知道老铁遇见过没有,日志打印了一大堆,真的去找导致异常和错误的一条没有。出现这个问题的根本原因是什么?就是因为系统没有一个规范的统一的异常规范。有的老铁发现异常后,直接e.printStackTrace() 打印出来堆栈就结束了,其实这样是很危险的。如果前期对异常没有统一的处理,后期在进行统一和调整真心非常非常的困难,异常跟我们的业务逻辑耦合的非常深的。调整统一过来非常非常的难。所以在设计系统的刚开始就必须设计的完善。如果对刚开始的系统异常和业务异常没有规范化,就算后期分布式进行监控的时候也是很困难的,监控系统无法知道是系统异常还是业务异常。无法准确的通知开发人员和运维人员。报警都不知道怎么报警,例如:一个密码输出错误的可能就引发报警,这样的结果是什么?报警量特别特别的大。如果不报警,到时候真出问题了,就玩完了。是不是 进退两难,说也不是,不说也不是。
场景回顾
- 老铁你遇到过不?
是不是总结的很经典。反正都不是我的锅,大家都是成年人了。
出现异常我们该怎么办
其实反映了问题,如果用户存在问题,只要提示明确,用户都不会反映到电话客服那里。如果还需要用户来反馈,说明系统异常设计的都不合理,我们监控系统没有做到位,用户都知道这个问题了,我们系统还不知道。监控系统需要做到的就是在用户反馈之前先知道。如果异常定义的很棒很坚挺的话,看到提示都知道问题出在哪里了!目前很多系统要定位一个问题需要花很长的时间。才能定位到哪里出的问题。现在咱们就搞明白,到底哪里发生了问题,达到一个目标,快速的响应,快速的知道,对应的问题,定位到问题的根本原因,对方传错了,有明确的指示。不应该把他带到线上,带上生产环境下,应该在上线之前就应该抹杀掉。
系统异常设计的出发点
- 良好的异常信息提示,开发运维人员能快速定位
- 响应外部调用异常时,应能明确指明是内部异常还是调用条件不满足导至。
- 响应用户操作异常时,能友好的提示用户。
异常分类
内部异常
响应没办法按照用户期待的结果返回。
- 资源环境导致(系统环境异常、数据库连接超时、第三方服务响应超时)
- 第三方服务错误响应
已经调入到第三方系统上去了,第三方的系统本身软件有bug,导致的
- 第三方响应结果错误
按照约定返回1和0,结果返回了-1。
- 外部传入参数非法
别人调用自身的系统,明确的告诉它参数传递错误。
- 错误的编码逻辑
调用参数,本来传递1-10,结果你传递了11。
- 错误的配置
上线代码链接的是测试数据库
- 异常的业务数据(业务数据缺失)
代码传递错误,custId 和 userId写反了。
业务异常
用户操作错误导致的,比如:密码错误。
- 用户操作错误
捕获异常。
- 业务条件不满足
业务的时候提前规范。
系统中正确的捕获这类异常,并抛出
1.方法入参进行合法性验证
- 对系统外部提供的接口(调用后立马验证,不要走了一段逻辑在进行验证),是必须要进行参数验证(必须)
- 系统内部对外外层提供接口,进行验证
- 工具类进行参数验证
- public 方法要进行验证
- private 方法(不建议参数验证)
2.第三方响应结果合法性验证
- 获取第三方法结果后,根据你们的约定进行验证
3.业务处理前,对业务业务前置条件进行验证
- 业务处理前,验证业务条件(验证佘额、验证这个帐户有没有被公安门锁定)
- 要考虑性能成本(验证身份证号码是不是存在的)
4.业务处理后,对处理结果进行验证
- 验证对方帐户是不是到帐了,转出帐户是不是成功扣款
5.对于可能会出现异常的代码进行 try catch 捕获
- 尝试恢复处理
- 直接抛出
- 转换后抛出
系统出口统一拦截处理
统一拦截的目的是确定出去的异常是可控的,调用方能够明白异常的信息,这里出口是指系统对外统一响应逻辑,一般我们可分三类场景。
1. Web Response
h5,pc页面
- 内部异常
引导至异常提示页
- 业务异常
返回对应提示消息至前端
- 未知异常
尝试进行识别,如果识别不了,转换成异常编码
2. Http API接口响应
- 内部异常
返回接口不可用消息
- 参数错误
基于API文档中的异常列表进行响应返回。表明参数非法,需要调用方法加强参数合法性校验
- 业务错误
基于非约定返回对应code与消息
3. RPC Service接口响应
- 内部异常
返回服务不可用消息
- 参数错误
基于接口文档进行响应,直接返回异常堆栈
- 业务错误
直接返回异常堆栈
checkedException 与uncheckedException 声明原则
- 如果是参数非法抛出,返回结果非法(即软件BUG) uncheckedException
- 如果你认为调用方程序员需要有意识地采取措施,那么抛出检查型异常。
- 程序产品有明确的条件约束的要求,可声明检测型业务异常
统一对异常进行分类处理
- 异常转换
- 异常信息处理
- 逻辑断言
- 参数合法性验证
- 返回结果合法性验证
异常捕获
统一对异常进行拦截处理
- 目的:防止不明确的异常流出系统
- RPC Service 响应拦截
- Web Control 响应拦截
- Http API 响应拦截
常见的错误的异常处理方式
- 直接勿略异常
try {
new String(source.getBytes("UTF-8"), "GBK");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
- 正确的处理
try {
new String(source.getBytes("UTF-8"), "GBK");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("环境不支持UTF-8",e)
}
- 业务异不提供任何信息
public class DuplicateUsernameException extends Exception {
}
- 给每个异常处理都定义一个Code,用一个统一异常替代所有业务异常
public class ServiceException extends RuntimeException {
public ServiceException(Exception e) {
super(e);
}
public ServiceException(String message) {
super(message);
}
}
错误:
1 、必须明确定义业务异常
2、 尽可能申明成checkedException 3要带上具体的业务数
正确方式:定义明确的业务异常
PS:健壮的系统异常的判断尤为重要,不要认为开发完成就完成了,其实在开发过程中,就像装修一样『前门』很光鲜,『后门』也得控制好。万一别人没从『前门』进来,要求让带个钥匙进门,结果拿个斧子进『后门』呢?