成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

dubbo源碼解析——Directory

neu / 1422人閱讀

摘要:首先來看一下接口的實(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(List urls) {
        // 聲明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

相關(guān)文章

  • dubbo源碼解析——消費(fèi)過程

    摘要:上一篇源碼解析概要篇中我們了解到中的一些概念及消費(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)行解析。...

    darkbug 評(píng)論0 收藏0
  • dubbo源碼解析(四)注冊(cè)中心——dubbo

    摘要:一該類繼承了類,該類里面封裝了一個(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)方式...

    andot 評(píng)論0 收藏0
  • dubbo源碼解析——概要篇

    摘要:服務(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è)谀?我們下一...

    Meathill 評(píng)論0 收藏0
  • dubbo源碼解析(四十五)服務(wù)引用過程

    摘要:服務(wù)引用過程目標(biāo)從源碼的角度分析服務(wù)引用過程。并保留服務(wù)提供者的部分配置,比如版本,,時(shí)間戳等最后將合并后的配置設(shè)置為查詢字符串中。的可以參考源碼解析二十三遠(yuǎn)程調(diào)用的一的源碼分析。 dubbo服務(wù)引用過程 目標(biāo):從源碼的角度分析服務(wù)引用過程。 前言 前面服務(wù)暴露過程的文章講解到,服務(wù)引用有兩種方式,一種就是直連,也就是直接指定服務(wù)的地址來進(jìn)行引用,這種方式更多的時(shí)候被用來做服務(wù)測(cè)試,不...

    xiaowugui666 評(píng)論0 收藏0
  • dubbo之SPI

    摘要:簡介全稱為,是一種服務(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)類的全限定名配置在文件中...

    UnixAgain 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

neu

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<