Dubbo的正确打开方式之基本接口定义及异常处理

为什么要写这篇文章呢?因为公司在使用Dubbo时并没有对Dubbo的用法做深入的了解,而是属于拿来就用,随着自己的想法来使用。这样很不好,就像天龙八部的鸠摩智练习错误的六脉神剑一样,方式不对,就容易走火入魔。下面是我近来在空闲时间对Dubbo的一些学习,也纠正了之前的一些错误用法,在此做一下记录:

接口定义及异常处理

原来错误的做法

先说说最初使用时的做法:
由于Provider和Consumer实际上就是一个服务端和一个客户端的关系。在实际应用当中,和APP直接交互的tomcat服务器就是客户端。那么在我们最初对Dubbo不了解的情况下,就照着APP接口的设计依样画葫芦,定义了一个ResponseDTO

public class ResponseDTO<T> implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 4082846602141879024L;

    private boolean status = true;

    private String msg;

    private Exception exception;

    private T data;
}

然后在Dubbo中提供接口时,会使用try...catch的方式,将异常信息塞到ResponseDTO中去。这样一来有几点坏处:

  • 增加了代码的重复度和复杂度
  • 可能会造成某些Exception在客户端无法被反序列化
  • 无法通过Dubbo提供的拦截器来处理异常

那么,其实我们应该怎么做呢?

直接就像定义普通方法那样定义接口就好了,无需关心异常情况。因为Dubbo已经默认提供了ExceptionFilter来帮助我们处理异常,这个Filter具体是用来干什么的呢?下面贴出核心代码,非常容易理解:

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)));

还记得刚才我们提出自己定义ResponseDTO的三个弊端吗?这里我们再列一下:

  • 增加了代码的重复度和复杂度
  • 可能会造成某些Exception在客户端无法被反序列化
  • 无法通过Dubbo提供的拦截器来处理异常

那么再看看Dubbo是如何通过ExceptionFilter来解决的:

  • Dubbo中提供接口时,我们无需每个方法都用try...catch包裹,然后组装ResponseDTO对象。而在客户端,也可以直接像调用本地方法一样调用Dubbo的方法,无需再处理ResponseDTO对象
  • 仔细看上面的核心代码,在解决的一个最核心的问题就是担心客户端无法反序列化,所以也就有了最后一行,将无法反序列化的Exception包装成RuntimeException
  • 我们可以通过自己写Filter来处理异常。最典型的,可能有的公司要求监控error日志,那么并不需要打error日志的异常我们可以通过重写Filter来实现。

所以对于接口定义和异常处理来说,正确的打开方式就是:

  1. 像定义普通方法一样定义接口,客户端直接调用即可
  2. 利用好Filter来处理异常信息
<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值