一、背景
在日常业务开发过程中,我们为了让业务代码更健壮,遇到错误时返回的提示更友好,一般会自定义一些业务异常。根据业务需要,分为自定义受检异常和非受检异常
1.1 知识点回顾
Exception类及其子类,但不包括?RuntimeException?的子类,统称为受检异常。如果方法执行过程中有可能抛出此类异常,必须在方法签名上声明
RuntimeException?类及其子类,统称为非受检异常。如果方法执行过程中有可能抛出此类异常,可以不必在方法签名上声明
1.2 开发问题
开发中遇到一个问题:Dubbo RPC调用时,provider抛出的一个业务类非受检异常,consumer接到时却是RuntimeException?并且message被和堆栈信息拼接到了一起。
二、问题原理
Dubbo?微服务中,provider?分为api和service,consumer只需要引入?api?从注册中心调用service?实例即可。
当service?中抛出一个自定义的非受检异常,且其相应api包中没有这个异常类时,就会出现异常被包装为RuntimeException的情况。
其实问题分析到这里,基本就有眉目了:Dubbo是一个RPC框架,客户端调用的都是远程方法,参数和返回值都是经过序列化和反序列化为字节数组传输的。consumer必须认识这个异常才能反序列化成功。
很明显,我们抛的这个异常?Dubbo认为consumer不认识,为了避免反序列化失败,从而对异常进行了包装。
下面结合源码阐述Dubbo的异常处理机制。
三、源码分析?
Dubbo?远程调用的异常由ExceptionFilter类处理
通过源码可以看到,该类的主要功能是返回接口抛出的异常,Dubbo将其定义为如下几种情况:
- 如果是checked异常,直接抛出
- 在方法签名上有声明,直接抛出
- 不符合1,2 的被认为是错误,会打印error日志,并尝试如下处理:
- 异常类和接口类在同一个jar包里,直接抛出
- 是JDK自带的异常,直接抛出
- 是Dubbo本身的异常,直接抛出
- 否则,包装成RuntimeException抛给客户端
事实上Dubbo作为RPC框架已经把各种抛异常的情况都考虑全了,最后如果Dubbo认为consumer不认识这个异常还会包装成RuntimeException兜底,防止反序列化失败。
如果发生了consumer找不到provider所抛异常的这种情况,不客气地讲,一定是开发者的问题,把这个归罪于Dubbo 那可就太冤枉它了!
?
|