文档章节

Dubbo抛出自定义异常

闪电
 闪电
发布于 2015/08/18 11:52
字数 720
阅读 9101
收藏 6

        最近开始学习Dubbo框架,在工作中会把之前的业务迁移过来。

        在原来的Spring MVC框架实现中,有使用到自定义异常的场景(自定义异常继承RuntimeException)。而对于异常(包括自定义异常),在业务代码中都不做任何try-catch操作,而是由公用的Controller来处理异常。

        在使用dubbo的过程中,在dubbo的service端定义有自定义异常进行throw的时候,却发现在customer的Controller中无法instanceof,自己自定义的异常类被转成了Runtime异常,有点不理解。在翻看dubbo源码的时候,发现确实如此:

public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            Result result = invoker.invoke(invocation);
            if (result.hasException() && GenericService.class != invoker.getInterface()) {
                try {
                    Throwable exception = result.getException();

                    // 如果是checked异常,直接抛出
                    if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) {
                        return result;
                    }
                    // 在方法签名上有声明,直接抛出
                    try {
                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
                        Class<?>[] exceptionClassses = method.getExceptionTypes();
                        for (Class<?> exceptionClass : exceptionClassses) {
                            if (exception.getClass().equals(exceptionClass)) {
                                return result;
                            }
                        }
                    } catch (NoSuchMethodException e) {
                        return result;
                    }

                    // 未在方法签名上定义的异常,在服务器端打印ERROR日志
                    logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                            + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                            + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);

                    // 异常类和接口类在同一jar包里,直接抛出
                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){
                        return result;
                    }
                    // 是JDK自带的异常,直接抛出
                    String className = exception.getClass().getName();
                    if (className.startsWith("java.") || className.startsWith("javax.")) {
                        return result;
                    }
                    // 是Dubbo本身的异常,直接抛出
                    if (exception instanceof RpcException) {
                        return result;
                    }

                    // 否则,包装成RuntimeException抛给客户端
                    return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
                } catch (Throwable e) {
                    logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
                            + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                            + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
                    return result;
                }
            }
            return result;
        } catch (RuntimeException e) {
            logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                    + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                    + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
            throw e;
        }
    }
        所以在dubbo的service端想抛出自定义异常,只能通过在service端的接口方法上声明所要抛出的异常,或者将异常类与接口同包,再或者是接口的实现类再实现dubbo的GenericService接口。

        对于第一种方案没有使用,因为它对代码的入侵比较严重。

        第二种方案可以实现,可对于目前的业务框架,让接口类和异常类同包则变得不太可能。

        所以最后选择了让接口实现类再实现GenericService接口,而对于其需要实现的$invoke方法则没有做任何的方法体处理,直接废弃。


        对于dubbo的service端自定义异常类的处理,有些不理解的就是,为什么dubbo需要对自定义异常类做一次Runtime异常的转化,而不是直接抛出原异常类型。或者有没有对dubbo更了解的朋友,有对自定义异常更好的处理方法。


版权声明:本文为博主原创文章,未经博主允许不得转载。

本文转载自:http://blog.csdn.net/xlee1905/article/details/44660449

闪电
粉丝 75
博文 392
码字总数 6789
作品 0
海淀
技术主管
私信 提问
加载中

评论(4)

呆呆1
如果上述几种方案涉及的改动较多,可以通过Filter来解决此问题

自定义一个消费端的Filter,在这里面判断,如果result.getException().getCause()是自定义的异常,那么就将这个getCause出来的对象转换为自定义异常,并用此异常重新构建Result返回
AGeek
AGeek
另外,回头看了下官方文档:
http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-%E6%9C%8D%E5%8A%A1%E5%8C%96%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5

这里告诉了异常处理的最佳实践:
- 建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,以及语义更友好,
- 如果担心性能问题,在必要时,可以通过override掉异常类的fillInStackTrace()方法为空方法,使其不拷贝栈信息,
- 查询方法不建议抛出checked异常,否则调用方在查询时将过多的try...catch,并且不能进行有效处理,
- 服务提供方不应将DAO或SQL等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常。
AGeek
AGeek
我也遇到这个问题,最后用方法后面加异常签名的方式做了。
我猜原因可能是由于 RuntimeException 只能在运行过程中抛出,Dubbo 并不知道服务方何时会抛出,而且客户端收到这个异常时自己的运行时上下文中可能并没有这个自定义异常的 scheme,不知道应该将异常报文反序列化成哪个异常,所以就用简单地用 RuntimeException 一锅端了。
而服务方法如果包含这个异常签名,或消费方调用的接口的包中包含这个异常定义,那就证明消费方有这个异常的定义,Dubbo 认为消费方就有能力将它反序列化,便直接抛出让代理去处理。
除非 Dubbo 能事先扫描服务方代码中的所有继承自 RuntimeException 的异常,并且在抛出异常前将该异常在消费方动态生成并 load 到运行时中,这个问题应该也可以解决,最好是能提供一个注解来定义哪些异常是客户端可能会收到的,在服务方启动时扫描注解,并广播到所有消费方动态 load,不过想想都麻烦。
面试官问我,使用Dubbo有没有遇到一些坑?我笑了。

前言 17年的时候,因为一时冲动没把持住(当然最近也有粉丝叫我再冲动一把再更新一波),结合面试题写了一个系列的Dubbo源码解析.目前公众号大部分粉丝都是之前的粉丝,这里不过多介绍. 根据我的面...

李红欧巴
03/09
49
0
面试官问我,使用 Dubbo 有没有遇到一些坑?我笑了。

本文转载自肥朝(订阅号id:feichao_java),经其本人同意授权转载。 17 年的时候,因为一时冲动没把持住(当然最近也有粉丝叫我再冲动一把再更新一波),结合面试题写了一个系列的 Dubbo 源码...

Qunar技术沙龙
05/24
0
0
从沉睡到满血复活,阿里开源框架 Dubbo 有没有让你踩过坑?

作者 | 肥朝 责编 | 郭芮 17年的时候,因为一时冲动没把持住,结合面试题写了一个系列的Dubbo源码解析,结果颇受欢迎。根据我的面试经验而言,能在简历上写上原理、源码等关键词的,都是非常...

CSDN资讯
03/19
0
0
Dubbo 2.0.9 发布,阿里巴巴开源服务框架

阿里巴巴开源服务框架Dubbo2.0.9版本发布了,该版本增加了简易监控中心界面,以及修复了一些BUG。 Dubbo首页:http://code.alibabatech.com/wiki/display/dubbo/Home 下载地址:http://code...

红薯
2011/12/14
15.4K
6
Dubbo消费端捕获到服务提供者抛出的自定义异常,e.getMessage(),返回并不是message信息,而是整个异常的堆栈信息,请问怎么解决?

Dubbo消费端捕获到服务提供者抛出的自定义异常,e.getMessage(),返回并不是message信息,而是整个异常的堆栈信息,请问怎么解决?我需要拿到message信息给前端弹出提示,可是通过e.getMessag...

颖辉小居
2018/11/05
565
3

没有更多内容

加载失败,请刷新页面

加载更多

JS基础-该如何理解原型、原型链?

JS的原型、原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯定不能对这个...

OBKoro1
今天
6
0
高防CDN的出现是为了解决网站的哪些问题?

高防CDN是为了更好的服务网络而出现的,是通过高防DNS来实现的。高防CDN是通过智能化的系统判断来路,再反馈给用户,可以减轻用户使用过程的复杂程度。通过智能DNS解析,能让网站访问者连接到...

云漫网络Ruan
今天
14
0
OSChina 周一乱弹 —— 熟悉的味道,难道这就是恋爱的感觉

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :好久没分享歌了分享张碧晨的单曲《今后我与自己流浪》 《今后我与自己流浪》- 张碧晨 手机党少年们想听歌,请使劲儿戳(这里)...

小小编辑
今天
2.7K
24
SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
46
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部