1. 程式人生 > >dubbo原始碼解析——Directory

dubbo原始碼解析——Directory

首先來看一下directory介面的實現類,他主要有兩個實現類,一個是StaticDirectory,一個是RegistryDirectory,本文主要解析RegistryDirectory。

StaticDirectory StaticDirectory中的Static關鍵詞來看,就知道,這個其實是不會動態變化的,從下圖知道,他的Invoker是通過建構函式傳入,StaticDirectory用得比較少,主要用在服務對多註冊中心的引用 在這裡插入圖片描述

RegistryDirectory 首先看下它的結構: 在這裡插入圖片描述 這個NotifyListener中的notify方法就是註冊中心的回撥,也就是它之所以能根據註冊中心動態變化的根源所在.

上文中,Directory的doList方法,這是一個抽象方法,通過呼叫子類RegistryDirectory的doList方法,從Directory中選擇出invoker列表。

@Override
    public List<Invoker<T>> 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<Invoker<T>> invokers = null; Map<String, List<Invoker<T>>> 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<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator(); if (iterator.hasNext()) { invokers = iterator.next(); } } } return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers; }

從中可以看出,Directory獲取invoker是從methodInvokerMap中獲取的。invoker什麼時候寫入到Directory的methodInvokerMap裡面呢?就是在回撥方法notify的時候操作的。

@Override
    public synchronized void notify(List<URL> urls) {
        // 宣告invoker的URL引用陣列
        List<URL> invokerUrls = new ArrayList<URL>();

        // 宣告router的URL引用陣列
        List<URL> routerUrls = new ArrayList<URL>();

        // 宣告configurator(配置器)的URL引用陣列
        List<URL> configuratorUrls = new ArrayList<URL>();

        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<Router> routers = toRouters(routerUrls);
            if (routers != null) { // null - do nothing
                setRouters(routers);
            }
        }
        List<Configurator> 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<URL> 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<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;

            if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
                // 如果傳入的invoker url是空的,從快取中新增
                invokerUrls.addAll(this.cachedInvokerUrls);
            } else {
                // 快取invoker url,便於比較
                this.cachedInvokerUrls = new HashSet<URL>();
                this.cachedInvokerUrls.addAll(invokerUrls);
            }
            if (invokerUrls.isEmpty()) {
                return;
            }
            // 把invoker url列表 轉換成 key為url,value為invoker的Map
            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
            // 把invoker url列表 轉換成 key為method,value為invoker的Map
            Map<String, List<Invoker<T>>> 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的值(這個後面講服務引用原理的時候會再提一下),這就是官網提到的它的值可能是動態變化的,比如註冊中心推送變更的原因所在