前面文章分析了服務的導出與引用過程,從本篇文章開始,我將開始分析 Dubbo 集羣容錯方面的源碼。這部分源碼包含四個部分,分別是服務目錄 Directory、服務路由 Router、集羣 Cluster 和負載均衡 LoadBalance。這幾個部分的源碼邏輯比較獨立,我會分四篇文章進行分析。本篇文章做爲集羣容錯的開篇文章,將和你們一塊兒分析服務目錄相關的源碼。在進行深刻分析以前,咱們先來了解一下服務目錄是什麼。服務目錄中存儲了一些和服務提供者有關的信息,經過服務目錄,服務消費者可獲取到服務提供者的信息,好比 ip、端口、服務協議等。經過這些信息,服務消費者就可經過 Netty 等客戶端進行遠程調用。在一個服務集羣中,服務提供者數量並非一成不變的,若是集羣中新增了一臺機器,相應地在服務目錄中就要新增一條服務提供者記錄。或者,若是服務提供者的配置修改了,服務目錄中的記錄也要作相應的更新。若是這樣說,服務目錄和註冊中心的功能不就雷同了嗎。確實如此,這裏這麼說是爲了方便你們理解。實際上服務目錄在獲取註冊中心的服務配置信息後,會爲每條配置信息生成一個 Invoker 對象,並把這個 Invoker 對象存儲起來,這個 Invoker 纔是服務目錄最終持有的對象。Invoker 有什麼用呢?看名字就知道了,這是一個具備遠程調用功能的對象。講到這你們應該知道了什麼是服務目錄了,它能夠看作是 Invoker 集合,且這個集合中的元素會隨註冊中心的變化而進行動態調整。html
好了,關於服務目錄這裏就先介紹這些,你們先有個大體印象便可。接下來咱們經過繼承體系圖來了解一下服務目錄的家族成員都有哪些。java
服務目錄目前內置的實現有兩個,分別爲 StaticDirectory 和 RegistryDirectory,它們均是 AbstractDirectory 的子類。AbstractDirectory 實現了 Directory 接口,這個接口包含了一個重要的方法定義,即 list(Invocation),用於列舉 Invoker。下面咱們來看一下他們的繼承體系圖。git
如上,Directory 繼承自 Node 接口,Node 這個接口繼承者比較多,像 Registry、Monitor、Invoker 等繼承了這個接口。這個接口包含了一個獲取配置信息的方法 getUrl,實現該接口的類能夠向外提供配置信息。另外,你們注意看 RegistryDirectory 實現了 NotifyListener 接口,當註冊中心節點信息發生變化後,RegistryDirectory 能夠經過此接口方法獲得變動信息,並根據變動信息動態調整內部 Invoker 列表。github
如今你們對服務目錄的繼承體系應該比較清楚了,下面咱們深刻到源碼中,探索服務目錄是如何實現的。apache
本章我將分析 AbstractDirectory 和它兩個子類的源碼。這裏之因此要分析 AbstractDirectory,而不是直接分析子類是有必定緣由的。AbstractDirectory 封裝了 Invoker 列舉流程,具體的列舉邏輯則由子類實現,這是典型的模板模式。因此,接下來咱們先來看一下 AbstractDirectory 的源碼。數組
public List<Invoker<T>> list(Invocation invocation) throws RpcException { if (destroyed) { throw new RpcException("Directory already destroyed..."); } // 調用 doList 方法列舉 Invoker,這裏的 doList 是模板方法,由子類實現 List<Invoker<T>> invokers = doList(invocation); // 獲取路由器 List<Router> localRouters = this.routers; if (localRouters != null && !localRouters.isEmpty()) { for (Router router : localRouters) { try { // 獲取 runtime 參數,並根據參數決定是否進行路由 if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) { // 進行服務路由 invokers = router.route(invokers, getConsumerUrl(), invocation); } } catch (Throwable t) { logger.error("Failed to execute router: ..."); } } } return invokers; } // 模板方法,由子類實現 protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException;
上面就是 AbstractDirectory 的 list 方法源碼,這個方法封裝了 Invoker 的列舉過程。以下:緩存
以上步驟中,doList 是模板方法,需由子類實現。Router 的 runtime 參數這裏簡單說明一下,這個參數決定了是否在每次調用服務時都執行路由規則。若是 runtime 爲 true,那麼每次調用服務前,都須要進行服務路由。這個對性能形成影響,慎重配置。關於該參數更詳細的說明,請參考官方文檔。負載均衡
介紹完 AbstractDirectory,接下來咱們開始分析子類的源碼。ide
StaticDirectory 即靜態服務目錄,顧名思義,它內部存放的 Invoker 是不會變更的。因此,理論上它和不可變 List 的功能很類似。下面咱們來看一下這個類的實現。源碼分析
public class StaticDirectory<T> extends AbstractDirectory<T> { // Invoker 列表 private final List<Invoker<T>> invokers; // 省略構造方法 @Override public Class<T> getInterface() { // 獲取接口類 return invokers.get(0).getInterface(); } // 檢測服務目錄是否可用 @Override public boolean isAvailable() { if (isDestroyed()) { return false; } for (Invoker<T> invoker : invokers) { if (invoker.isAvailable()) { // 只要有一個 Invoker 是可用的,就職務當前目錄是可用的 return true; } } return false; } @Override public void destroy() { if (isDestroyed()) { return; } // 調用父類銷燬邏輯 super.destroy(); // 遍歷 Invoker 列表,並執行相應的銷燬邏輯 for (Invoker<T> invoker : invokers) { invoker.destroy(); } invokers.clear(); } @Override protected List<Invoker<T>> doList(Invocation invocation) throws RpcException { // 列舉 Inovker,也就是直接返回 invokers 成員變量 return invokers; } }
以上就是 StaticDirectory 的代碼邏輯,很簡單,你們都能看懂,我就很少說了。下面來看看 RegistryDirectory,這個類的邏輯比較複雜。
RegistryDirectory 是一種動態服務目錄,它實現了 NotifyListener 接口。當註冊中心服務配置發生變化後,RegistryDirectory 可收到與當前服務相關的變化。收到變動通知後,RegistryDirectory 可根據配置變動信息刷新 Invoker 列表。RegistryDirectory 中有幾個比較重要的邏輯,第一是 Invoker 的列舉邏輯,第二是接受服務配置變動的邏輯,第三是 Invoker 的刷新邏輯。接下來,我將按順序對這三塊邏輯。
Invoker 列舉邏輯封裝在 doList 方法中,這是個模板方法,前面已經介紹過了。那這裏就不過多囉嗦了,咱們直入主題吧。
public List<Invoker<T>> doList(Invocation invocation) { if (forbidden) { // 服務提供者關閉或禁用了服務,此時拋出 No provider 異常 throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry ..."); } List<Invoker<T>> invokers = null; // 獲取 Invoker 本地緩存 Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) { // 獲取方法名和參數列表 String methodName = RpcUtils.getMethodName(invocation); Object[] args = RpcUtils.getArguments(invocation); // 檢測參數列表的第一個參數是否爲 String 或 enum 類型 if (args != null && args.length > 0 && args[0] != null && (args[0] instanceof String || args[0].getClass().isEnum())) { // 經過 方法名 + 第一個參數名稱 查詢 Invoker 列表,具體的使用場景暫時沒想到 invokers = localMethodInvokerMap.get(methodName + "." + args[0]); } if (invokers == null) { // 經過方法名獲取 Invoker 列表 invokers = localMethodInvokerMap.get(methodName); } if (invokers == null) { // 經過星號 * 獲取 Invoker 列表 invokers = localMethodInvokerMap.get(Constants.ANY_VALUE); } if (invokers == null) { Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator(); if (iterator.hasNext()) { // 經過迭代器獲取 Invoker 列表 invokers = iterator.next(); } } } // 返回 Invoker 列表 return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers; }
以上代碼進行屢次嘗試,以期從 localMethodInvokerMap 中獲取到 Invoker 列表。通常狀況下,普通的調用可經過方法名獲取到對應的 Invoker 列表,泛化調用可經過 * 獲取到 Invoker 列表。按現有的邏輯,無論什麼狀況下,* 到 Invoker 列表的映射關係 <*, invokers> 老是存在的,也就意味着 localMethodInvokerMap.get(Constants.ANY_VALUE) 老是有值返回。除非這個值是 null,纔會經過經過迭代器獲取 Invoker 列表。至於什麼狀況下爲空,我暫時未徹底搞清楚,我猜想是被路由規則(用戶可基於 Router 接口實現自定義路由器)處理後,可能會獲得一個 null。目前僅是猜想,未作驗證。
本節的邏輯主要是從 localMethodInvokerMap 中獲取 Invoker,localMethodInvokerMap 源自 RegistryDirectory 類的成員變量 methodInvokerMap。doList 方法能夠看作是對 methodInvokerMap 變量的讀操做,至於對 methodInvokerMap 變量的寫操做,這個將在後續進行分析。
RegistryDirectory 是一個動態服務目錄,它須要接受註冊中心配置進行動態調整。所以 RegistryDirectory 實現了 NotifyListener 接口,經過這個接口獲取註冊中心變動通知。下面咱們來看一下具體的邏輯。
public synchronized void notify(List<URL> urls) { // 定義三個集合,分別用於存放服務提供者 url,路由 url,配置器 url List<URL> invokerUrls = new ArrayList<URL>(); List<URL> routerUrls = new ArrayList<URL>(); List<URL> configuratorUrls = new ArrayList<URL>(); for (URL url : urls) { String protocol = url.getProtocol(); // 獲取 category 參數 String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); // 根據 category 參數將 url 分別放到不一樣的列表中 if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) { // 添加路由器 url routerUrls.add(url); } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) { // 添加配置器 url configuratorUrls.add(url); } else if (Constants.PROVIDERS_CATEGORY.equals(category)) { // 添加服務提供者 url invokerUrls.add(url); } else { // 忽略不支持的 category logger.warn("Unsupported category ..."); } } if (configuratorUrls != null && !configuratorUrls.isEmpty()) { // 將 url 轉成 Configurator this.configurators = toConfigurators(configuratorUrls); } if (routerUrls != null && !routerUrls.isEmpty()) { // 將 url 轉成 Router List<Router> routers = toRouters(routerUrls); if (routers != null) { setRouters(routers); } } List<Configurator> localConfigurators = this.configurators; this.overrideDirectoryUrl = directoryUrl; if (localConfigurators != null && !localConfigurators.isEmpty()) { for (Configurator configurator : localConfigurators) { // 配置 overrideDirectoryUrl this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } // 刷新 Invoker 列表 refreshInvoker(invokerUrls); }
如上,notify 方法首先是根據 url 的 category 參數對 url 進行分門別類存儲,而後經過 toRouters 和 toConfigurators 將 url 列表轉成 Router 和 Configurator 列表。最後調用 refreshInvoker 方法刷新 Invoker 列表。這裏的 toRouters 和 toConfigurators 方法邏輯不復雜,你們自行分析。接下來,咱們把重點放在 refreshInvoker 方法上。
接着上一節繼續分析,refreshInvoker 方法是保證 RegistryDirectory 隨註冊中心變化而變化的關鍵所在。這一塊邏輯比較多,接下來一一進行分析。
private void refreshInvoker(List<URL> invokerUrls) { // invokerUrls 僅有一個元素,且 url 協議頭爲 empty,此時表示禁用全部服務 if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { // 設置 forbidden 爲 true this.forbidden = true; this.methodInvokerMap = null; // 銷燬全部 Invoker destroyAllInvokers(); } else { this.forbidden = false; Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) { // 添加緩存 url 到 invokerUrls 中 invokerUrls.addAll(this.cachedInvokerUrls); } else { this.cachedInvokerUrls = new HashSet<URL>(); // 緩存 invokerUrls this.cachedInvokerUrls.addAll(invokerUrls); } if (invokerUrls.isEmpty()) { return; } // 將 url 轉成 Invoker Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls); // 將 newUrlInvokerMap 轉成方法名到 Invoker 列表的映射 Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 轉換出錯,直接打印異常,並返回 if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) { logger.error(new IllegalStateException("urls to invokers error ...")); return; } // 合併多個組的 Invoker this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap; // 保存爲本地緩存 this.urlInvokerMap = newUrlInvokerMap; try { // 銷燬無用 Invoker destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); } catch (Exception e) { logger.warn("destroyUnusedInvokers error. ", e); } } }
上面方法的代碼不是不少,可是邏輯卻很多。首先時根據入參 invokerUrls 的數量和協議頭判斷是否禁用全部的服務,若是禁用,則將 forbidden 設爲 true,並銷燬全部的 Invoker。若不由用,則將 url 轉成 Invoker,獲得 <url, Invoker> 的映射關係。而後進一步進行轉換,獲得 <methodName, Invoker 列表>。以後進行多組 Invoker 合併操做,並將合併結果賦值給 methodInvokerMap。methodInvokerMap 變量在 doList 方法中會被用到,doList 會對該變量進行讀操做,在這裏是寫操做。當新的 Invoker 列表生成後,還要一個重要的工做要作,就是銷燬無用的 Invoker,避免服務消費者調用已下線的服務的服務。
接下里,我將對上面涉及到的調用進行分析。按照順序,這裏先來分析 url 到 Invoker 的轉換過程。
private Map<String, Invoker<T>> toInvokers(List<URL> urls) { Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>(); if (urls == null || urls.isEmpty()) { return newUrlInvokerMap; } Set<String> keys = new HashSet<String>(); // 獲取服務消費端配置的協議 String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY); for (URL providerUrl : urls) { if (queryProtocols != null && queryProtocols.length() > 0) { boolean accept = false; String[] acceptProtocols = queryProtocols.split(","); // 檢測服務提供者協議是否被服務消費者所支持 for (String acceptProtocol : acceptProtocols) { if (providerUrl.getProtocol().equals(acceptProtocol)) { accept = true; break; } } if (!accept) { // 若服務消費者協議頭不被消費者所支持,則忽略當前 providerUrl continue; } } // 忽略 empty 協議 if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) { continue; } // 經過 SPI 檢測服務端協議是否被消費端支持 if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) { logger.error(new IllegalStateException("Unsupported protocol...")); continue; } // 合併 url URL url = mergeUrl(providerUrl); String key = url.toFullString(); if (keys.contains(key)) { // 忽略重複 url continue; } keys.add(key); // 本地 Invoker 緩存列表 Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key); // 緩存未命中 if (invoker == null) { try { boolean enabled = true; if (url.hasParameter(Constants.DISABLED_KEY)) { // 獲取 disable 配置,並修改 enable 變量 enabled = !url.getParameter(Constants.DISABLED_KEY, false); } else { enabled = url.getParameter(Constants.ENABLED_KEY, true); } if (enabled) { // 調用 refer 獲取 Invoker invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl); } } catch (Throwable t) { logger.error("Failed to refer invoker for interface..."); } if (invoker != null) { // 緩存 Invoker 實例 newUrlInvokerMap.put(key, invoker); } } else { // 緩存命中,將 invoker 存儲到 newUrlInvokerMap 中 newUrlInvokerMap.put(key, invoker); } } keys.clear(); return newUrlInvokerMap; }
toInvokers 方法一開始會對服務提供者 url 進行檢測,若服務消費端的配置不支持服務端的協議,或服務端 url 協議頭爲 empty 時,toInvokers 均會忽略服務提供方 url。必要的檢測作完後,緊接着是合併 url,而後訪問緩存,嘗試獲取與 url 對應的 invoker。若是緩存命中,直接將 Invoker 存入 newUrlInvokerMap 中便可。若是未命中,則須要新建 Invoker。Invoker 是經過 Protocol 的 refer 方法建立的,這個我在上一篇文章中已經分析過了,這裏就不贅述了。
toInvokers 方法返回的是 <url, Invoker> 映射關係表,接下來還要對這個結果進行進一步處理,獲得方法名到 Invoker 列表的映射關係。這個過程由 toMethodInvokers 方法完成,以下:
private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) { // 方法名 -> Invoker 列表 Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>(); List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>(); if (invokersMap != null && invokersMap.size() > 0) { for (Invoker<T> invoker : invokersMap.values()) { // 獲取 methods 參數 String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY); if (parameter != null && parameter.length() > 0) { // 切分 methods 參數值,獲得方法名數組 String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter); if (methods != null && methods.length > 0) { for (String method : methods) { // 方法名不爲 * if (method != null && method.length() > 0 && !Constants.ANY_VALUE.equals(method)) { // 根據方法名獲取 Invoker 列表 List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method); if (methodInvokers == null) { methodInvokers = new ArrayList<Invoker<T>>(); newMethodInvokerMap.put(method, methodInvokers); } // 存儲 Invoker 到列表中 methodInvokers.add(invoker); } } } } invokersList.add(invoker); } } // 進行服務級別路由,參考:https://github.com/apache/incubator-dubbo/pull/749 List<Invoker<T>> newInvokersList = route(invokersList, null); // 存儲 <*, newInvokersList> 映射關係 newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList); if (serviceMethods != null && serviceMethods.length > 0) { for (String method : serviceMethods) { List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method); if (methodInvokers == null || methodInvokers.isEmpty()) { methodInvokers = newInvokersList; } // 進行方法級別路由 newMethodInvokerMap.put(method, route(methodInvokers, method)); } } // 排序,轉成不可變列表 for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) { List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method); Collections.sort(methodInvokers, InvokerComparator.getComparator()); newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers)); } return Collections.unmodifiableMap(newMethodInvokerMap); }
上面方法主要作了三件事情, 第一是對入參進行遍歷,而後獲取 methods 參數,並切分紅數組。隨後以方法名爲鍵,Invoker 列表爲值,將映射關係存儲到 newMethodInvokerMap 中。第二是分別基於類和方法對 Invoker 列表進行路由操做。第三是對 Invoker 列表進行排序,並轉成不可變列表。關於 toMethodInvokers 方法就先分析到這,咱們繼續向下分析,此次要分析的多組服務的合併邏輯。
private Map<String, List<Invoker<T>>> toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap) { Map<String, List<Invoker<T>>> result = new HashMap<String, List<Invoker<T>>>(); // 遍歷入參 for (Map.Entry<String, List<Invoker<T>>> entry : methodMap.entrySet()) { String method = entry.getKey(); List<Invoker<T>> invokers = entry.getValue(); // group -> Invoker 列表 Map<String, List<Invoker<T>>> groupMap = new HashMap<String, List<Invoker<T>>>(); // 遍歷 Invoker 列表 for (Invoker<T> invoker : invokers) { // 獲取分組配置 String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, ""); List<Invoker<T>> groupInvokers = groupMap.get(group); if (groupInvokers == null) { groupInvokers = new ArrayList<Invoker<T>>(); // 緩存 <group, List<Invoker>> 到 groupMap 中 groupMap.put(group, groupInvokers); } // 存儲 invoker 到 groupInvokers groupInvokers.add(invoker); } if (groupMap.size() == 1) { // 若是 groupMap 中僅包含一組鍵值對,此時直接取出該鍵值對的值便可 result.put(method, groupMap.values().iterator().next()); // groupMap 中包含多組鍵值對,好比: // { // "dubbo": [invoker1, invoker2, invoker3, ...], // "hello": [invoker4, invoker5, invoker6, ...] // } } else if (groupMap.size() > 1) { List<Invoker<T>> groupInvokers = new ArrayList<Invoker<T>>(); for (List<Invoker<T>> groupList : groupMap.values()) { // 經過集羣類合併每一個分組對應的 Invoker 列表 groupInvokers.add(cluster.join(new StaticDirectory<T>(groupList))); } // 緩存結果 result.put(method, groupInvokers); } else { result.put(method, invokers); } } return result; }
上面方法首先是生成 group 到 Invoker 類比的映射關係表,若關係表中的映射關係數量大於1,表示有多組服務。此時經過集羣類合併每組 Invoker,並將合併結果存儲到 groupInvokers 中。以後將方法名與 groupInvokers 存到到 result 中,並返回,整個邏輯結束。
接下來咱們再來看一下 Invoker 列表刷新邏輯的最後一個動做 -- 刪除無用 Invoker。以下:
private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) { if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) { destroyAllInvokers(); return; } List<String> deleted = null; if (oldUrlInvokerMap != null) { // 獲取新生成的 Invoker 列表 Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values(); // 遍歷老的 <url, Invoker> 映射表 for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) { // 檢測 newInvokers 中是否包含老的 Invoker if (!newInvokers.contains(entry.getValue())) { if (deleted == null) { deleted = new ArrayList<String>(); } // 若不包含,則將老的 Invoker 對應的 url 存入 deleted 列表中 deleted.add(entry.getKey()); } } } if (deleted != null) { // 遍歷 deleted 集合,併到老的 <url, Invoker> 映射關係表查出 Invoker,銷燬之 for (String url : deleted) { if (url != null) { // 從 oldUrlInvokerMap 中移除 url 對應的 Invoker Invoker<T> invoker = oldUrlInvokerMap.remove(url); if (invoker != null) { try { // 銷燬 Invoker invoker.destroy(); } catch (Exception e) { logger.warn("destroy invoker..."); } } } } } }
destroyUnusedInvokers 方法的主要邏輯是經過 newUrlInvokerMap 找出待刪除 Invoker 對應的 url,並將 url 存入到 deleted 列表中。而後再遍歷 deleted 列表,並從 oldUrlInvokerMap 中移除相應的 Invoker,銷燬之。整個邏輯大體如此,不是很難理解。
到此關於 Invoker 列表的刷新邏輯就分析了,這裏對整個過程進行簡單總結。以下:
Invoker 的刷新邏輯仍是比較複雜的,你們在看的過程當中多寫點 demo 進行調試。好了,本節就到這。
本篇文章對 Dubbo 服務目錄進行了較爲詳細的分析,篇幅主要集中在 RegistryDirectory 的源碼分析上。分析下來,不禁得感嘆,想讓本地服務目錄和註冊中心保持一致仍是須要作不少事情的,並不簡單。服務目錄是 Dubbo 集羣容錯的一部分,也是比較基礎的部分,因此你們務必搞懂。
好了,本篇文章就先到這了。感謝你們閱讀。
本文在知識共享許可協議 4.0 下發布,轉載需在明顯位置處註明出處
做者:田小波
本文同步發佈在個人我的博客:http://www.tianxiaobo.com
本做品採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。