详解Dubbo没法处理自定义异常及解决方案

问题描述

Dubbo有一个比较奇怪的问题,目前不知道Apache和Alibaba公司出于什么样的考虑,貌似一直都没有一个比较合适的解决方案,问题以下:java

  • 项目搭建中你须要自定义一个本地的Exception,命名为好比BusinessException。比较通常的书写代码以下:安全

    /** * @Author linqiang * @Date 2019/10/24 16:20 * @Version 1.0 * @Description 业务异常类 **/
    public class BusinessException extends RuntimeException {
        private Integer code;
        private String msg;
    
        public BusinessException(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public String getMsg() {
            return msg;
        }
    }
    复制代码
  • 一般这个BusinessException是要可以跨模块使用的,通常放在commons或者core模块中,同时别的模块的pom.xml文件引入这些模块,使得整个项目均可以使用这个BusinessException。app

  • 问题来了,若是在A模块调用了B模块,B模块抛出了一个BusinessException,这时A模块接收到的不是BusinessException,而是一个RuntimeException,并且关于BusinessException的细节已经彻底丢失,只会剩下一个类名的描述。ide

问题缘由

关于该问题出现的缘由,参考这篇文章,概括一下,就是在Dubbo的传输信息过程当中,类ExceptionFilter.java会对Exception作一个过滤,其过滤器的关键代码以下:this

// directly throw if it's checked exception
if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
    return;
}
// directly throw if the exception appears in the signature
try {
    Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
    Class<?>[] exceptionClassses = method.getExceptionTypes();
    for (Class<?> exceptionClass : exceptionClassses) {
        if (exception.getClass().equals(exceptionClass)) {
            return;
        }
    }
} catch (NoSuchMethodException e) {
    return;
}

// for the exception not found in method's signature, print ERROR message in server's log.
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);

// directly throw if exception class and interface class are in the same jar file.
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
    return;
}
// directly throw if it's JDK exception
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
    return;
}

// directly throw if it's dubbo exception
if (exception instanceof RpcException) {
    return;
}

// otherwise, wrap with RuntimeException and throw back to the client
appResponse.setException(new RuntimeException(StringUtils.toString(exception)));
return;
复制代码

即Dubbo在遇到异常时会这样处理:spa

  • 非RuntimeException不处理,直接返回
  • 抛出的是方法上注明的异常,直接返回
  • 若是异常类和接口类在同一jar包,直接返回
  • java或者javax目录下的异常类,直接返回
  • Dubbo自带的RpcException,直接返回
  • 其余的异常,会被封装为RuntimeException返回

解决方式

根据以上的分析,那么很显然,自定义异常是被直接封装为RuntimeException返回了,并且只带了自定义异常的类名信息,丢失了别的细节。.net

那么咱们想要自定义异常进行正常返回,那只有知足这个FIlter所写的上述条件。咱们能够分析一下:设计

  • 不继承RuntimeException,以检查时异常抛出。不推荐,正常的业务异常应该是运行时异常。code

  • 在接口方法上要写上throws BusinessException,以下:server

    public interface DemoService {
    
        DemoUser getUserInfo(Long userID) throws BusinessException;
    
    }
    复制代码

    不推荐,不符合接口设计原则,且这样是把运行时异常做为检查时异常处理。

  • 把自定义异常类和接口放在同一个包目录下。不推荐,毕竟这样至关于绑定了异常类的目录,耦合性变高。

  • 改包名,以“java.”或者“javax.”来开头。不推荐,违反了类命名原则。

  • 继承Dubbo的RpcException。RpcException也是继承了RuntimeException,所以可以以RuntimeException的方式进行处理。不推荐,至关于自定义异常属于Dubbo的RpcException,这在程序设计上不合理。

咱们发现,想要知足Dubbo的过滤器直接返回异常的条件,咱们就必须作出一些违反程序设计的操做,若是必定要从这些方法中选择一种的话,相对来讲,自定义异常类和接口放在同一目录下,以及继承RpcException是对于程序侵入性更小的方式。

其余解决方式

参考 这篇文章,提供了两种解决方式:

1.在配置文件中配置以下,效果是:关闭ExceptionFIlter,使全部异常绕过该过滤器直接返回。不推荐,Dubbo既然设置了这个异常过滤类,必定是出于安全和功能上的考虑,直接禁用可能会引起别的问题。

dubbo:
 provider:
 filter: -exception
复制代码

2.修改Dubbo源文件ExceptionFilter,使其遇到BusinessException也能直接返回。不推荐,至关于定制了本地的Dubbo包,是一个后续很容易被人忽略的大坑。

总结

Dubbo在处理自定义异常时,会直接返回RuntimeException,且抹去自定义异常的全部细节,致使没法处理。

本文写下的时候,Dubbo版本为2.7.3,该问题尚未很是完美的解决方案,相对来讲,把自定义异常和接口类放在同一目录下是侵入性最小的方案。

相关文章
相关标签/搜索