关于Dubbo泛化调用返回自定义异常实现

缘由:使用dubbo的过程当中遇到provider返回的自定义异常被包装,在使用泛化调用的过程当中,异常被包装成了GenericException(以前也在开发的过程当中也遇到了,还不是太懂,没有去解决)

文章源码及部分讲解源自《深刻理解Apache Dubbo实战》
dubbo version:2.7.1前端

在各类百度的过程当中,和我预期的不太同样。找到了一篇看着还行的文章,先留着,看其余的文章时发现都是如出一辙的,并且版本停留的比较前,不过关于dubbo的泛化调用也一直没怎么变。java

Filter简单描述

过滤器链初始化的实现原理

  1. 扩展点的初始化(SPI)
  2. 过滤器链组装:
    在服务暴露的过程当中会使用Protocol层,ProtocolFilterWrapper实现组装。在暴露与引用的过程当中,会使用ProtocolFilterWrapper#buildInvokerChain方法组装整个过滤器apache

    服务暴露与引用的过程

// 1-服务暴露时会调用buildInvokerChain
@Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        // Constants.PROVIDER->根据Constants.PROVIDER标识本身是提供者类型的调用链(group)
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }
    // 引用远程服务的时候也会调用buildInvokerChain
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        // Constants.CONSUMER->根据Constants.CONSUMER标识消费者类型的调用链(group)
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }

#### 构造调用链源码json

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;// 保存引用,后续用于把真正的调用者保存在过滤器的最后
        // 得到全部的过滤器
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (!filters.isEmpty()) {
            // 对过滤器作倒排遍历,即从尾到头。在构造的过程当中会从一直往上构造,因此调用顺序仍是头为第一个节点
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                // 把last节点变成next节点,并放到Filter链的next中
                final Invoker<T> next = last;
                last = new Invoker<T>() {
                    ···省略
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        // 设置过滤器链的下一个节点,不断循环造成过滤链
                        Result result = filter.invoke(next, invocation);
                        // 异步调用和同步调用的处理
                        if (result instanceof AsyncRpcResult) {
                            AsyncRpcResult asyncResult = (AsyncRpcResult) result;
                            asyncResult.thenApplyWithContext(r -> filter.onResponse(r, invoker, invocation));
                            return asyncResult;
                        } else {
                            return filter.onResponse(result, invoker, invocation);
                        }
                    }

                    @Override
                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }

GenericFilter

用于服务提供者端,实现泛化调用,实现序列化的检查和处理
Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
        if (result.hasException()
                && !(result.getException() instanceof GenericException)) {
            return new RpcResult(new GenericException(result.getException()));
        }

上述代码是实现Filter#invoke方法,在执行完后会判断Result中是否有异常,若不是GenericException会包装为GenericException异常再返回。app

我在提供者作了自定义异常的处理,可是被包装了,并不能直接返回到前端被捕获。异步

下面我加入了判断:async

if (result.hasException()
                && !(result.getException() instanceof GenericException)) {
            // 处理自定义异常
            String className = result.getException().getClass().getName();
            if (className.startsWith("com.changyuan.education.commons.exception")) {
                // 打印堆栈
                result.getException().printStackTrace();
                // 返回异常信息
                return new RpcResult(new ResultBean().failure(result.getException().toString()));
            }
            return new RpcResult(new GenericException(result.getException()));
        }
  1. new ResultBean() 自定义的返回前端的对象,包含code,msg,data
  2. result.getException().toString() 获取我抛出的自定义异常的内容,为了与ResultBean元素同样包含code,msg
// 重写的toString,转为json对象
 @Override
    public String toString() {
        return "{\"code\":" + code + ",\"msg\":\"" + msg + "\"}";
    }
  1. resultBean()中接受json并解析,最后打印完异常抛出给前端
public ResultBean<T> failure(String upExcetionJson) {
        JSONObject jsonObject = JSON.parseObject(upExcetionJson);
        this.code = (int) jsonObject.get("code");
        this.msg = jsonObject.get("msg");
        return this;
    }
  1. 对于其余的java的异常先放着,后续解决,先把已知的交互异常处理

Dubbo2GenericFilter 自定义的SPI

  1. 先建立这么一个过滤器
@Activate(group = Constants.PROVIDER, order = -20000)
public class Dubbo2GenericFilter implements Filter {
}
  1. 在resources中建立文件夹META-INF.dubbo.internal,建立文件org.apache.dubbo.rpc.Filter,写入一下内容ide

    generic=com.changyuan.education.exam.filter.Dubbo2GenericFilter

扩展点的配置和规范

Dubbo SPI 和Java SPI相似,须要在META-INF/dubbo/下放置对应的SPI配置文件,文件名称必须命名为接口的全路径名。配置文件的内容为key=扩展点实现类的全路径名,多个实现用换行符隔开。其中key会被做为Dubbo SPI注解中传入的参数。Dubbo会默认扫描三个文件夹META-INF/dubbo/、META-INF/services/、META-INF/dubbo/internal/ui

相关文章
相关标签/搜索