小伙伴去面试,碰到面试官问:"你对Dubbo源码这么熟悉,那请问你使用的时候,有没有遇到什么坑?"
毫无准备的他,菊花顿时一紧!此时就面临 唬住了50K,唬不住就只能5K的局面,慌了!
如何反杀
相信大家面试都遇到过类似问题,因为源码解析网上很多,很多人"考前突击"一下,但是遇到喜欢问细节的面试官,终究难逃法眼,无处遁形.遇到这个问题,我们如何反杀一波?那么我就从一次聊天记录说起,只有拥有真实场景的源码实战(非常重要),遇到这类问题,才不至于出现 猛虎落泪的情形
真实场景描述
那么我们把业务相关去掉,抽取一个最简模型.我们在公司,一般都会有自己的自定义异常,然后这个自定义异常一般放在common.jar给其他模块依赖,比如我这里定义一个HelloException
![](https://img.haomeiwen.com/i17890920/1ca31fd9ab448225.png)
然后我们写一个最简单的Dubbo的demo,如下
interface
![](https://img.haomeiwen.com/i17890920/80333f1ac8808560.png)
provider
![](https://img.haomeiwen.com/i17890920/c2828bb881b1f7ed.png)
consumer
![](https://img.haomeiwen.com/i17890920/cb104474230a672c.png)
按照聊天记录的描述,此时consumer调用provider,provider抛出HelloException.但是consumer捕获到的,却不是HelloException.
那么我们运行看看
果然如该同事所言.为什么会这样呢?之前没看过我的Dubbo源码解析系列的同学这种时候往往采用最低效的解决办法,把异常栈往微信群一丢,各种求助.但是往往毫无收获,然后感叹社会为何如此冷漠!
但是相信公众号的老粉丝们早已掌握阅读源码的技能,和我一样坐怀不乱,九浅一深直入源码.出现异常我们首先看一下异常栈
这行异常和我一样,就像漆黑中的萤火虫一样,那么鲜明,那么出众
1com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:108)
那么我们一探究竟
![](https://img.haomeiwen.com/i17890920/bcd53e050c26ac63.png)
![](https://img.haomeiwen.com/i17890920/1b925913668d253b.png)
手机上阅读源码或许并不友好,但是没关系,上面都有完善的中文注释,他想表达的意思如下:
1、如果是checked异常,直接抛出.很明显,我们的HelloException是RuntimeException,不符合
2、在方法签名上有声明,直接抛出.很明显,我们接口并未声明该异常,不符合
3、异常类和接口类在同一jar包里,直接抛出.很明显,我们的异常类是在common.jar的,接口是在api.jar的,不符合
4、是JDK自带的异常,直接抛出.很明显,这个HelloException是我们自定义的,不符合
5、是Dubbo本身的异常(RpcException),直接抛出.很明显,这个HelloException是我们自定义的,和RpcException几乎没有半毛钱关系.
6、否则,包装成RuntimeException抛给客户端.因为以上5点均不满足,所以该异常会被包装成RuntimeException异常抛出(重要)
这也就是为什么我们catchHelloException是catch不到的,因为他包装成RuntimeException了
Dubbo为什么这么设计
也许你看到这里会觉得这个判断好坑.Dubbo为什么要这么设计?我们看源码,最重要的是知道作者为什么这么设计,只有知道为什么这么设计才是经过了深度的思考,否则看时高潮,看后就忘.
其实Dubbo的这个考虑,是基于序列化来考虑的.你想想,如果provider抛出一个仅在provider自定义的一个异常,那么该异常到达consumer,明显是无法序列化的.所以你注意看Dubbo的判断.我们来看下他的判断
1、checked异常和RuntimeException是不同类型,强行包装可能会出现类型转换错误,因此不包,直接抛出
2、方法签名上有声明.方法签名上有声明,如果这个异常是provider.jar中定义的,因为consumer是依赖api.jar的,而不是依赖provider.jar.那么编译都编译不过,如果能编译得过,说明consumer是能依赖到这个异常的,因此序列化不会有问题,直接抛出
3、异常类和接口类在同一jar包里.provider和consumer都依赖api,如果异常在这个api,那序列化也不会有问题,直接抛出
4、是JDK自带的异常,直接抛出.provider和consumer都依赖jdk,序列化也不会有问题,直接抛出
5、是Dubbo本身的异常(RpcException),直接抛出.provider和consumer都依赖Dubbo,序列化也不会有问题,直接抛出
6、否则,包装成RuntimeException抛给客户端.此时,就有可能出现我说的那种,这个异常是provider.jar自定义的,那么provider抛出的时候进行序列化,因为consumer没有依赖provider.jar,所以异常到达consumer时,根本无法反序列化.但是包装成了 RuntimeException异常则不同,此时异常就是JDK中的类了,到哪都能序列化.
如何解决
既然都知道了原理了,那么很好解决,我随便列举一下,比如从规范上要求业务方接口声明HelloException
网友评论