摘要:首先來看一下接口的實(shí)現(xiàn)類他主要有兩個(gè)實(shí)現(xiàn)類一個(gè)是一個(gè)是,本文主要解析。如果傳入的列表為空,則意味著該規(guī)則僅是重寫規(guī)則或路由規(guī)則,需要對(duì)其進(jìn)行重新對(duì)比以決定是否重新引用。
首先來看一下directory接口的實(shí)現(xiàn)類,他主要有兩個(gè)實(shí)現(xiàn)類,一個(gè)是StaticDirectory,一個(gè)是RegistryDirectory,本文主要解析RegistryDirectory。
StaticDirectory
StaticDirectory中的Static關(guān)鍵詞來看,就知道,這個(gè)其實(shí)是不會(huì)動(dòng)態(tài)變化的,從下圖知道,他的Invoker是通過構(gòu)造函數(shù)傳入,StaticDirectory用得比較少,主要用在服務(wù)對(duì)多注冊(cè)中心的引用
RegistryDirectory
首先看下它的結(jié)構(gòu):
這個(gè)NotifyListener中的notify方法就是注冊(cè)中心的回調(diào),也就是它之所以能根據(jù)注冊(cè)中心動(dòng)態(tài)變化的根源所在.
上文中,Directory的doList方法,這是一個(gè)抽象方法,通過調(diào)用子類RegistryDirectory的doList方法,從Directory中選擇出invoker列表。
@Override public List> doList(Invocation invocation) { if (forbidden) { // 如果forbidden為true,則沒有服務(wù)提供者 或者 服務(wù)提供者不可用 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); // 獲取方法參數(shù) Object[] args = RpcUtils.getArguments(invocation); if (args != null && args.length > 0 && args[0] != null && (args[0] instanceof String || args[0].getClass().isEnum())) { // 如果第一個(gè)參數(shù)是字符串類型 或者 枚舉類型 // 可以根據(jù)第一個(gè)參數(shù)枚舉路由 invokers = localMethodInvokerMap.get(methodName + "." + args[0]); } if (invokers == null) { // 仍然為null,使用方法名稱獲取 invokers = localMethodInvokerMap.get(methodName); } if (invokers == null) { // 仍讓為null,使用 * 隨機(jī)取一個(gè)invoker來實(shí)現(xiàn)調(diào)用 invokers = localMethodInvokerMap.get(Constants.ANY_VALUE); } if (invokers == null) { // 仍然為null,使用迭代器獲取一個(gè)invoker Iterator >> iterator = localMethodInvokerMap.values().iterator(); if (iterator.hasNext()) { invokers = iterator.next(); } } } return invokers == null ? new ArrayList
>(0) : invokers; }
從中可以看出,Directory獲取invoker是從methodInvokerMap中獲取的。invoker什么時(shí)候?qū)懭氲紻irectory的methodInvokerMap里面呢?就是在回調(diào)方法notify的時(shí)候操作的。
@Override public synchronized void notify(Listurls) { // 聲明invoker的URL引用數(shù)組 List invokerUrls = new ArrayList (); // 聲明router的URL引用數(shù)組 List routerUrls = new ArrayList (); // 聲明configurator(配置器)的URL引用數(shù)組 List configuratorUrls = new ArrayList (); for (URL url : urls) { // 獲取協(xié)議名稱 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)) { // 如果是路由分類 或者 路由協(xié)議 routerUrls.add(url); } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) { // 如果是配置器分類 或者 重寫協(xié)議 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參數(shù) this.overrideDirectoryUrl = directoryUrl; if (localConfigurators != null && !localConfigurators.isEmpty()) { for (Configurator configurator : localConfigurators) { this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } // 刷新providers refreshInvoker(invokerUrls); } /** * 把invokerUrl列表 轉(zhuǎn)化成 invoker Map。轉(zhuǎn)換規(guī)則如下 * 1.如果URL已經(jīng)轉(zhuǎn)換為invoker,則不再重新引用它,并且直接從緩存中獲得它,并且注意URL中的任何參數(shù)更改都將被重新引用。 * 2.如果傳入的invoker列表不是空的,則意味著它是最新的調(diào)用列表。 * 3.如果傳入的invokerUrl列表為空,則意味著該規(guī)則僅是重寫規(guī)則或路由規(guī)則,需要對(duì)其進(jìn)行重新對(duì)比以決定是否重新引用。 * @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())) { // 只有一個(gè)invoker url,并且協(xié)議是空協(xié)議 // 設(shè)置禁止訪問 this.forbidden = true; // 設(shè)置methodInvokerMap為null this.methodInvokerMap = null; // 關(guān)閉所有invoker destroyAllInvokers(); } else { // 設(shè)置允許訪問 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列表 轉(zhuǎn)換成 key為url,value為invoker的Map Map > newUrlInvokerMap = toInvokers(invokerUrls); // 把invoker url列表 轉(zhuǎn)換成 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; } // 如果有多個(gè)分組,就合并methodInvokerMap this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap; this.urlInvokerMap = newUrlInvokerMap; try { // 關(guān)閉沒有使用invoker destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); } catch (Exception e) { logger.warn("destroyUnusedInvokers error. ", e); } } }
也就是注冊(cè)中心有變化,則更新methodInvokerMap和urlInvokerMap的值(這個(gè)后面講服務(wù)引用原理的時(shí)候會(huì)再提一下),這就是官網(wǎng)提到的它的值可能是動(dòng)態(tài)變化的,比如注冊(cè)中心推送變更的原因所在
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72099.html
摘要:上一篇源碼解析概要篇中我們了解到中的一些概念及消費(fèi)端總體調(diào)用過程。由于在生成代理實(shí)例的時(shí)候,在構(gòu)造函數(shù)中賦值了,因此可以只用該進(jìn)行方法的調(diào)用。 上一篇 dubbo源碼解析——概要篇中我們了解到dubbo中的一些概念及消費(fèi)端總體調(diào)用過程。本文中,將進(jìn)入消費(fèi)端源碼解析(具體邏輯會(huì)放到代碼的注釋中)。本文先是對(duì)消費(fèi)過程的總體代碼邏輯理一遍,個(gè)別需要細(xì)講的點(diǎn),后面會(huì)專門的文章進(jìn)行解析。...
摘要:一該類繼承了類,該類里面封裝了一個(gè)重連機(jī)制,而注冊(cè)中心核心的功能注冊(cè)訂閱取消注冊(cè)取消訂閱,查詢注冊(cè)列表都是調(diào)用了我上一篇文章源碼解析三注冊(cè)中心開篇中講到的實(shí)現(xiàn)方法,畢竟這種實(shí)現(xiàn)注冊(cè)中心的方式是默認(rèn)的方式,不過推薦使用,這個(gè)后續(xù)講解。 注冊(cè)中心——dubbo 目標(biāo):解釋以為dubbo實(shí)現(xiàn)的注冊(cè)中心原理,解讀duubo-registry-default源碼 dubbo內(nèi)置的注冊(cè)中心實(shí)現(xiàn)方式...
摘要:服務(wù)提供者代碼上面這個(gè)類會(huì)被封裝成為一個(gè)實(shí)例,并新生成一個(gè)實(shí)例。這樣當(dāng)網(wǎng)絡(luò)通訊層收到一個(gè)請(qǐng)求后,會(huì)找到對(duì)應(yīng)的實(shí)例,并調(diào)用它所對(duì)應(yīng)的實(shí)例,從而真正調(diào)用了服務(wù)提供者的代碼。 這次源碼解析借鑒《肥朝》前輩的dubbo源碼解析,進(jìn)行源碼學(xué)習(xí)。總結(jié)起來就是先總體,后局部.也就是先把需要注意的概念先拋出來,把整體架構(gòu)圖先畫出來.讓讀者拿著地圖跟著我的腳步,并且每一步我都提醒,現(xiàn)在我們?cè)谀?我們下一...
摘要:服務(wù)引用過程目標(biāo)從源碼的角度分析服務(wù)引用過程。并保留服務(wù)提供者的部分配置,比如版本,,時(shí)間戳等最后將合并后的配置設(shè)置為查詢字符串中。的可以參考源碼解析二十三遠(yuǎn)程調(diào)用的一的源碼分析。 dubbo服務(wù)引用過程 目標(biāo):從源碼的角度分析服務(wù)引用過程。 前言 前面服務(wù)暴露過程的文章講解到,服務(wù)引用有兩種方式,一種就是直連,也就是直接指定服務(wù)的地址來進(jìn)行引用,這種方式更多的時(shí)候被用來做服務(wù)測(cè)試,不...
摘要:簡介全稱為,是一種服務(wù)發(fā)現(xiàn)機(jī)制。的本質(zhì)是將接口實(shí)現(xiàn)類的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實(shí)現(xiàn)類。不過,并未使用原生的機(jī)制,而是對(duì)其進(jìn)行了增強(qiáng),使其能夠更好的滿足需求。并未使用,而是重新實(shí)現(xiàn)了一套功能更強(qiáng)的機(jī)制。 1、SPI簡介 SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。SPI 的本質(zhì)是將接口實(shí)現(xiàn)類的全限定名配置在文件中...
閱讀 3476·2021-11-17 17:00
閱讀 3840·2021-08-09 13:46
閱讀 2880·2019-08-30 15:54
閱讀 648·2019-08-30 13:54
閱讀 2959·2019-08-29 17:13
閱讀 3238·2019-08-29 14:00
閱讀 2993·2019-08-29 11:11
閱讀 1404·2019-08-26 10:15