Dubbo异常处理

本文详细介绍了Dubbo中异常处理的机制,特别是针对自定义异常的处理方式。Dubbo通过ExceptionFilter确保只有预期的异常被客户端接收,而其他异常则会被转换为RuntimeException,以避免客户端反序列化错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title: Dubbo异常处理 tags:

  • dubbo
  • 异常
  • filter
  • exception
  • 自定义异常 categories: dubbo date: 2017-06-25 18:18:53

小伙伴在使用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. 显示声明异常在方法签名
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值