Dubbo客户端返回自定义异常

虽然上一篇解决了message过长的问题 Dubbo自定义异常message过长解决

但是小伙伴仍然反映对于异常不能使用原类型而导致邮件报错太多(邮件报错基于错误类型来的,比如定义的某种参数异常不需要要发邮件)

而基于上文的分析代码中不支持返回是添加Attachment。

那么只能通过其他途径了。

/**
 * Created by qixiaobo on 2017/7/3.
 */
@Activate(group = Constants.PROVIDER, before = {"exception"}, value = {"customException"})
public class CustomExceptionFilter implements Filter {
    private static Logger logger = LoggerFactory.getLogger(CustomExceptionFilter.class);
 
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        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;
                }
                // 是JDK自带的异常,直接抛出
                String className = exception.getClass().getName();
                if (className.startsWith("java.") || className.startsWith("javax.")) {
                    return result;
                }
                // 是Dubbo本身的异常,直接抛出
                if (exception instanceof RpcException) {
                    return result;
                }
                //其他exception ,减少问题,直接将exception序列化成RuntimeException,同时放入指定的异常类型值attachment中
                // 否则,包装成RuntimeException抛给客户端
                result = new RpcResult(new RuntimeException(exception.getClass().getName()+"#"+exception.getMessage()));
            } catch (Throwable e) {
                logger.error(e.getMessage(), e);
                return result;
            }
        }
        return result;
    }
}

和上文相比将异常类型藏在了Exception的message中

@Activate(group = Constants.CONSUMER, value = {"clientException"})
public class ClientExceptionFilter implements Filter {
    private static Logger logger = LoggerFactory.getLogger(ClientExceptionFilter.class);
 
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Result result = invoker.invoke(invocation);
        if (result.hasException()) {
            Throwable exception = result.getException();
            if ((exception instanceof RuntimeException)) {
                //可能是自定义异常
                String message = exception.getMessage();
                if (exception.getMessage() != null) {
                    String[] messages = message.split("#", 2);
                    if (messages.length == 2) {
                        try {
                            logger.debug("message:{}", message);
                            Class exceptionClass = Class.forName(messages[0]);
                            try {
                                Constructor messageConstructor = exceptionClass.getConstructor(String.class);
                                messageConstructor.setAccessible(true);
                                exception = (Throwable) messageConstructor.newInstance(messages[1]);
                            } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
                                try {
                                    logger.debug(e.getMessage(), e);
                                    Constructor constructor = exceptionClass.getConstructor();
                                    constructor.setAccessible(true);
                                    exception = (Throwable) constructor.newInstance();
                                } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e1) {
                                    logger.debug(e1.getMessage(), e1);
                                }
                            }
                            if (result instanceof RpcResult) {
                                ((RpcResult) result).setException(exception);
                            }
                        } catch (ClassNotFoundException e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
 
                }
            }
        }
        return result;
    }
}

自定义客户端的filter,当应用该filter时

  1. 只校验RuntimeException(上文中异常均背转化为RuntimeException)
  2. 拆分RuntimeException
  3. 将类型取出后直接转化为异常类,调用器构造函数(优先尝试message,其次尝试默认构造函数)
  4. 将异常重新塞回RpcResult

在客户端的dubbo声明中如下

<dubbo:consumer timeout="60000" group="${dubbo.group}" retries="0" owner="qixiaobo" id="f6-consumer" filter="clientException"/>

在抛弃堆栈信息后序列化数据变少,并且客户端无感知

要求该异常在客户端必须存在,否则仍然是RuntimeException

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JavaEdge

SpringMVC数据绑定定义支持的数据绑定方式

定义 百度百科定义: 简单绑定是将一个用户界面元素(控件)的属性绑定到一个类型(对象)实例上的某个属性的方法。 例如,如果一个开发者有一个Customer类...

4456
来自专栏十月梦想

JavaScript数据类型

null,undefined,boolean,number,string,object

1013
来自专栏后端沉思录

自定义参数解析器

开发中,app端给服务端会传基础参数、其他参数,一般基础参数app端都会传给服务端,其他参数则是根据不同接口传不同参数。若以表单的形式提交的数据:

1603
来自专栏码匠的流水账

聊聊flink的MemoryBackendCheckpointStorage

本文主要研究一下flink的MemoryBackendCheckpointStorage

772
来自专栏函数式编程语言及工具

Scalaz(19)- Monad: \/ - Monad 版本的 Either

  scala标准库提供了一个Either类型,它可以说是Option的升级版。与Option相同,Either也有两种状态:Left和Right,分别对应Op...

2895
来自专栏函数式编程语言及工具

Scalaz(50)- scalaz-stream: 安全的无穷运算-running infinite stream freely

scalaz-stream支持无穷数据流(infinite stream),这本身是它强大的功能之一,试想有多少系统需要通过无穷运算才能得以实现。这是因为外界...

1946
来自专栏技术墨客

Spring和性——数据的类型转换

在字符串到实体转换一文中介绍了Spring核心框架中使用PropertyEditor将任何字符串转换为数字、实体的方法。除了字符串到实体,Spring还提供了更...

1783
来自专栏冰霜之地

iOS如何优雅的处理“回调地狱Callback hell”(二)——使用Swift

在上篇中,我谈到了可以用promise来解决Callback hell的问题,这篇我们换一种方式一样可以解决这个问题。

1722
来自专栏海天一树

小朋友学C++(14):两数交换

之前学C语言的时候,咱们直接在main函数中使用“异或”位运算符,很容易实现了两数交换。 本节课将在此基础上,把交换两个数的算法,封装到swap函数中。这样不管...

2847
来自专栏vue

装箱和拆箱

 装箱和拆箱       1、装箱:值类型----->引用类型       2、拆箱:引用类型----->值类型       3、我们判断是否发生了拆箱或者装箱...

1164

扫码关注云+社区