關於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

相關文章
相關標籤/搜索