GenericException
(以前也在開發的過程當中也遇到了,還不是太懂,沒有去解決)文章源碼及部分講解源自《深刻理解Apache Dubbo實戰》 dubbo version:2.7.1
前端
在各類百度的過程當中,和我預期的不太同樣。找到了一篇看着還行的文章,先留着,看其餘的文章時發現都是如出一轍的,並且版本停留的比較前,不過關於dubbo的泛化調用也一直沒怎麼變。java
過濾器鏈組裝:
在服務暴露的過程當中會使用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; }
用於服務提供者端,實現泛化調用,實現序列化的檢查和處理
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())); }
// 重寫的toString,轉爲json對象 @Override public String toString() { return "{\"code\":" + code + ",\"msg\":\"" + msg + "\"}"; }
public ResultBean<T> failure(String upExcetionJson) { JSONObject jsonObject = JSON.parseObject(upExcetionJson); this.code = (int) jsonObject.get("code"); this.msg = jsonObject.get("msg"); return this; }
@Activate(group = Constants.PROVIDER, order = -20000) public class Dubbo2GenericFilter implements Filter { }
在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