dubbo处理自定义异常问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25673113/article/details/78574514

最近在项目上遇到一个有关dubbo的问题,项目分为两层:下层是一些基础服务,上层是业务服务调用下层的基础服务。上层服务的有一个全局的异常拦截器来拦截异常。

@ControllerAdvice
@Slf4j
public class ExceptionFilter {

    @ExceptionHandler
    public ResponseEntity defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {

        if(e instanceof MyException){
            //自定义处理
        }

        log.error(e.getMessage(), e);
        Result res = new Result(500, "SYSTEM_EXCEPTIONS", "系统异常");
        return ResponseEntity.ok(res);
    }
}

全局异常可以catch程序上层的异常,进行不同的处理,进行更加友好的提示。
下层抛出的异常:

public void text(){
    throw new MyException("aaaa");
}

然后遇到了一个奇怪的问题,在下层中抛出了一个自定义的异常,但是在上层中捕获异常的时候,却是捕获到runtimeException异常,异常中的message存放了我们需要的对象异常。导致就无法对自定义的异常做特殊处理。

java.lang.RuntimeException: MyException: aaa

查阅了一下资料以后是因为dubbo对异常进行了处理,如果Dubbo的暴露抛出异常(Throwable),则会被暴露方的ExceptionFilter拦截到,执行以下invoke方法:

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

从代码可以看出,duubo只会抛出受检异常、jdk自身的异常、方法上申明的异常、dubbo自带的RpcException异常等等,其他的都会包装成new RuntimeException(StringUtils.toString(exception))抛出,所以也有了我遇到的问题。

只要的发生问题的原因就可以解决问题:

  • 可以在暴露方法的时候声明异常
  • 可以把异常设置为受检异常
  • 可以把异常和api包放在同一个jar包中
  • provider实现GenericService接口
  • 自定义异常继承dubbo的异常类

上述方法是在ExceptionFilter的基础上,可以换个思路,通过订制错误码,把遗产放在对象中进行处理,不过这个思路对使用方的改动较大,需要在开始定义架构的时候的时候进行约定。也可以使用修改配置文件跳过ExceptionFilter这个拦截器,但是这样做暴露方会把所有的原始异常暴露给使用者。

跳过异常拦截器的配置

    <dubbo:provider ....... ... filter="-exception"/>

猜你喜欢

转载自blog.csdn.net/qq_25673113/article/details/78574514