Dubbo异常处理

作者: 3c69b7c624d9 | 来源:发表于2017-11-30 23:00 被阅读20次

小伙伴在使用dubbo过程中推出了疑问,部分自定义异常不见了,变成了RuntimeException。并且message超长。

如下图

我们来查看一下Dubbo中如何对待自定义异常的。来到ExceptionFilter

    /**
     * ExceptionInvokerFilter
     * <p>
     * 功能:
     * <ol>
     * <li>不期望的异常打ERROR日志(Provider端)<br>
     *     不期望的日志即是,没有的接口上声明的Unchecked异常。
     * <li>异常不在API包中,则Wrap一层RuntimeException。<br>
     *     RPC对于第一层异常会直接序列化传输(Cause异常会String化),避免异常在Client出不能反序列化问题。
     * </ol>
     *
     * @author william.liangf
     * @author ding.lid
     */
    @Activate(group = Constants.PROVIDER)
    public class ExceptionFilter implements Filter {
     
        private final Logger logger;
         
        public ExceptionFilter() {
            this(LoggerFactory.getLogger(ExceptionFilter.class));
        }
         
        public ExceptionFilter(Logger logger) {
            this.logger = logger;
        }
         
        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;
            }
        }
     
    }
  1. 如果是受检异常直接抛出(当然可以,因为必须在签名中声明,如果客户端不引用异常包直接编译报错)
  2. 如果方法签名上引用,直接抛出(和1一样的原因)
  3. 判断接口api和抛出的exception是否在一个jar,是直接抛出
  4. 如果是jdk的异常直接抛出(其实部分jar不遵守该约定,使用了java或者javax的包,可能出错哦,客户端端无法反序列化)
  5. 如果是Dubbo本身的异常直接抛出(此处代码乍看有问题,instanceof RpcException 不能表示一定是Dubbo自定义的,因为可以继承。可是深入查看该异常为final,不可以继承。)
  6. 否则将异常转换为RuntimeException

因此出现了如上图的结果。导致在系统中可能出现异常转化为RuntimeException,并且会将堆栈信息一起写入message,导致message超长。(可以通过复写异常类的fillStackTrace来解决过长的问题)

对于自定义异常可能可以做到将异常和接口包装在同一个jar。但是部分依赖外部异常可能无法做到。

解决方案

  1. 在异常返回到消费者之前提前包装成自定义异常(自定义异常和接口打在同一个jar中)
  2. 显示声明异常在方法签名

相关文章

网友评论

    本文标题:Dubbo异常处理

    本文链接:https://www.haomeiwen.com/subject/zqobbxtx.html