摘要:首先來看一下接口的實現類他主要有兩個實現類一個是一個是,本文主要解析。如果傳入的列表為空,則意味著該規則僅是重寫規則或路由規則,需要對其進行重新對比以決定是否重新引用。
首先來看一下directory接口的實現類,他主要有兩個實現類,一個是StaticDirectory,一個是RegistryDirectory,本文主要解析RegistryDirectory。
StaticDirectory
StaticDirectory中的Static關鍵詞來看,就知道,這個其實是不會動態變化的,從下圖知道,他的Invoker是通過構造函數傳入,StaticDirectory用得比較少,主要用在服務對多注冊中心的引用
RegistryDirectory
首先看下它的結構:
這個NotifyListener中的notify方法就是注冊中心的回調,也就是它之所以能根據注冊中心動態變化的根源所在.
上文中,Directory的doList方法,這是一個抽象方法,通過調用子類RegistryDirectory的doList方法,從Directory中選擇出invoker列表。
@Override public List> doList(Invocation invocation) { if (forbidden) { // 如果forbidden為true,則沒有服務提供者 或者 服務提供者不可用 throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist)."); } List > invokers = null; Map >> localMethodInvokerMap = this.methodInvokerMap; if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) { // 獲取方法名稱 String methodName = RpcUtils.getMethodName(invocation); // 獲取方法參數 Object[] args = RpcUtils.getArguments(invocation); if (args != null && args.length > 0 && args[0] != null && (args[0] instanceof String || args[0].getClass().isEnum())) { // 如果第一個參數是字符串類型 或者 枚舉類型 // 可以根據第一個參數枚舉路由 invokers = localMethodInvokerMap.get(methodName + "." + args[0]); } if (invokers == null) { // 仍然為null,使用方法名稱獲取 invokers = localMethodInvokerMap.get(methodName); } if (invokers == null) { // 仍讓為null,使用 * 隨機取一個invoker來實現調用 invokers = localMethodInvokerMap.get(Constants.ANY_VALUE); } if (invokers == null) { // 仍然為null,使用迭代器獲取一個invoker Iterator >> iterator = localMethodInvokerMap.values().iterator(); if (iterator.hasNext()) { invokers = iterator.next(); } } } return invokers == null ? new ArrayList
>(0) : invokers; }
從中可以看出,Directory獲取invoker是從methodInvokerMap中獲取的。invoker什么時候寫入到Directory的methodInvokerMap里面呢?就是在回調方法notify的時候操作的。
@Override public synchronized void notify(Listurls) { // 聲明invoker的URL引用數組 List invokerUrls = new ArrayList (); // 聲明router的URL引用數組 List routerUrls = new ArrayList (); // 聲明configurator(配置器)的URL引用數組 List configuratorUrls = new ArrayList (); for (URL url : urls) { // 獲取協議名稱 String protocol = url.getProtocol(); // 獲取分類 String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) { // 如果是路由分類 或者 路由協議 routerUrls.add(url); } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) { // 如果是配置器分類 或者 重寫協議 configuratorUrls.add(url); } else if (Constants.PROVIDERS_CATEGORY.equals(category)) { // 如果是提供方分類 invokerUrls.add(url); } else { logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()); } } // 通過url獲取configurators if (configuratorUrls != null && !configuratorUrls.isEmpty()) { this.configurators = toConfigurators(configuratorUrls); } // 通過url獲取routers if (routerUrls != null && !routerUrls.isEmpty()) { List routers = toRouters(routerUrls); if (routers != null) { // null - do nothing setRouters(routers); } } List localConfigurators = this.configurators; // 合并override參數 this.overrideDirectoryUrl = directoryUrl; if (localConfigurators != null && !localConfigurators.isEmpty()) { for (Configurator configurator : localConfigurators) { this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } // 刷新providers refreshInvoker(invokerUrls); } /** * 把invokerUrl列表 轉化成 invoker Map。轉換規則如下 * 1.如果URL已經轉換為invoker,則不再重新引用它,并且直接從緩存中獲得它,并且注意URL中的任何參數更改都將被重新引用。 * 2.如果傳入的invoker列表不是空的,則意味著它是最新的調用列表。 * 3.如果傳入的invokerUrl列表為空,則意味著該規則僅是重寫規則或路由規則,需要對其進行重新對比以決定是否重新引用。 * @param invokerUrls this parameter can"t be null */ private void refreshInvoker(List invokerUrls) { if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { // 只有一個invoker url,并且協議是空協議 // 設置禁止訪問 this.forbidden = true; // 設置methodInvokerMap為null this.methodInvokerMap = null; // 關閉所有invoker destroyAllInvokers(); } else { // 設置允許訪問 this.forbidden = false; Map > oldUrlInvokerMap = this.urlInvokerMap; if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) { // 如果傳入的invoker url是空的,從緩存中添加 invokerUrls.addAll(this.cachedInvokerUrls); } else { // 緩存invoker url,便于比較 this.cachedInvokerUrls = new HashSet (); this.cachedInvokerUrls.addAll(invokerUrls); } if (invokerUrls.isEmpty()) { return; } // 把invoker url列表 轉換成 key為url,value為invoker的Map Map > newUrlInvokerMap = toInvokers(invokerUrls); // 把invoker url列表 轉換成 key為method,value為invoker的Map Map >> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) { logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString())); return; } // 如果有多個分組,就合并methodInvokerMap this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap; this.urlInvokerMap = newUrlInvokerMap; try { // 關閉沒有使用invoker destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); } catch (Exception e) { logger.warn("destroyUnusedInvokers error. ", e); } } }
也就是注冊中心有變化,則更新methodInvokerMap和urlInvokerMap的值(這個后面講服務引用原理的時候會再提一下),這就是官網提到的它的值可能是動態變化的,比如注冊中心推送變更的原因所在
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72099.html
摘要:上一篇源碼解析概要篇中我們了解到中的一些概念及消費端總體調用過程。由于在生成代理實例的時候,在構造函數中賦值了,因此可以只用該進行方法的調用。 上一篇 dubbo源碼解析——概要篇中我們了解到dubbo中的一些概念及消費端總體調用過程。本文中,將進入消費端源碼解析(具體邏輯會放到代碼的注釋中)。本文先是對消費過程的總體代碼邏輯理一遍,個別需要細講的點,后面會專門的文章進行解析。...
摘要:一該類繼承了類,該類里面封裝了一個重連機制,而注冊中心核心的功能注冊訂閱取消注冊取消訂閱,查詢注冊列表都是調用了我上一篇文章源碼解析三注冊中心開篇中講到的實現方法,畢竟這種實現注冊中心的方式是默認的方式,不過推薦使用,這個后續講解。 注冊中心——dubbo 目標:解釋以為dubbo實現的注冊中心原理,解讀duubo-registry-default源碼 dubbo內置的注冊中心實現方式...
摘要:服務提供者代碼上面這個類會被封裝成為一個實例,并新生成一個實例。這樣當網絡通訊層收到一個請求后,會找到對應的實例,并調用它所對應的實例,從而真正調用了服務提供者的代碼。 這次源碼解析借鑒《肥朝》前輩的dubbo源碼解析,進行源碼學習。總結起來就是先總體,后局部.也就是先把需要注意的概念先拋出來,把整體架構圖先畫出來.讓讀者拿著地圖跟著我的腳步,并且每一步我都提醒,現在我們在哪,我們下一...
摘要:服務引用過程目標從源碼的角度分析服務引用過程。并保留服務提供者的部分配置,比如版本,,時間戳等最后將合并后的配置設置為查詢字符串中。的可以參考源碼解析二十三遠程調用的一的源碼分析。 dubbo服務引用過程 目標:從源碼的角度分析服務引用過程。 前言 前面服務暴露過程的文章講解到,服務引用有兩種方式,一種就是直連,也就是直接指定服務的地址來進行引用,這種方式更多的時候被用來做服務測試,不...
閱讀 3457·2021-11-17 17:00
閱讀 3818·2021-08-09 13:46
閱讀 2866·2019-08-30 15:54
閱讀 627·2019-08-30 13:54
閱讀 2945·2019-08-29 17:13
閱讀 3218·2019-08-29 14:00
閱讀 2975·2019-08-29 11:11
閱讀 1379·2019-08-26 10:15