dubbo路由機制代碼分析1

這回說說,dubbo路由特性,dubbo的路由乾的事,就是一個請求過來,
dubbo依據配置的路由規則,計算出哪些提供者能夠提供此次的請求服務。
因此,它的優先級是在集羣容錯策略和負載均衡策略以前的。
即先有路由規則遴選出符合條件的服務提供者
而後,再在這些服務提供者之中應用負載均衡,集羣容錯策略。
流程圖大概是以下圖:java

能夠經過代碼驗證上面的邏輯。
看AbstractClusterInvoker類invoke方法,服務調用的入口方法:app

public Result invoke(final Invocation invocation) throws RpcException {

        checkWhetherDestroyed();

        LoadBalance loadbalance;
        //list方法
        //會調用directory的list方法。
        //實際上是AbstractDirectory的list方法,這個方法裏就是利用路由規則(若是有),從全部
        //提供者中,遴選出符合規則的提供者s.
        //接下里纔是集羣容錯和負載均衡。
        //能夠發現每次調用這些過程都從新下走一遍這個邏輯
        List<Invoker<T>> invokers = list(invocation);
        if (invokers != null && invokers.size() > 0) {
            //從url經過key "loadbalance" 取不到值,就取默認random隨機策略
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(invocation.getMethodName(), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        } else {
            //取默認random隨機策略
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
        }
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        return doInvoke(invocation, invokers, loadbalance);
    }

list方法的具體實現,在AbstractDirectory類裏負載均衡

/***
     * 落地路由規則
     * @param invocation
     * @return
     * @throws RpcException
     */
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException("Directory already destroyed .url: " + getUrl());
        }
        //獲取全部的提供者
        List<Invoker<T>> invokers = doList(invocation);
        //本地路由規則,這個其實已經過setRouters方法設置好。何時設置的,稍後看看
        List<Router> localRouters = this.routers; // local reference
        if (localRouters != null && localRouters.size() > 0) {
            for (Router router : localRouters) {
                try {
                    if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
                        //依次經過路由器的濾路由規則,最後返回invokers
                        invokers = router.route(invokers, getConsumerUrl(), invocation);
                    }
                } catch (Throwable t) {
                    logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
                }
            }
        }
        return invokers;
    }

路由器在哪裏設置的?還在AbstractDirectory類裏setRouters方法dom

/***
     * 設置路由器
     * @param routers
     */
    protected void setRouters(List<Router> routers) {
        // copy list
        routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
        //append url router
        //獲取路由key
	//這裏感受是從consumer端配置router的路由規則,可是consumer沒有router配置的地方???
	//官方文檔給出<dubbo:protocol router="xxx" />配置方式,可是並無實現。
        String routerkey = url.getParameter(Constants.ROUTER_KEY);
        if (routerkey != null && routerkey.length() > 0) {
           //根據routerkey獲取三種路由工廠的其中一種
            RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
           //再由路由工廠獲取具體類型路由器
            routers.add(routerFactory.getRouter(url));
        }
        // append mock invoker selector
        // 追加mock invoker 路由器,
        routers.add(new MockInvokersSelector());
	//排序後MockInvokersSelector會放在最後
	//MockInvokersSelector路由器,是dubbo對mock調用支持的一部分,稍後看下源碼
        Collections.sort(routers);
        this.routers = routers;
    }

setRouters在哪裏調用的?在AbstractDirectory子類。
RegistryDirectory類,能夠看到notify裏有,notify是註冊中心通知consumer回調的方法ide

public synchronized void notify(List<URL> urls) {
        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();
            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());
            }
        }
        // configurators
        if (configuratorUrls != null && configuratorUrls.size() > 0) {
            this.configurators = toConfigurators(configuratorUrls);
        }
        // routers
        if (routerUrls != null && routerUrls.size() > 0) {
	   //把路由配置,裝換成路由器實例
            List<Router> routers = toRouters(routerUrls);
            if (routers != null) { // null - do nothing
	        //設置了路由。
                setRouters(routers);
            }
        }
        List<Configurator> localConfigurators = this.configurators; // local reference
        // 合併override參數
        this.overrideDirectoryUrl = directoryUrl;
        if (localConfigurators != null && localConfigurators.size() > 0) {
            for (Configurator configurator : localConfigurators) {
                this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
            }
        }
        // providers
        refreshInvoker(invokerUrls);
    }

   這樣就保證配置能夠從註冊中心下發到調用方。
   能夠看到,這裏有個 toRouters(routerUrls)方法把路由配置轉換成路由器實例
 this

/**
     * 把路由配置轉化成具體路由器
     * @param urls
     * @return null : no routers ,do nothing
     * else :routers list
     */
    private List<Router> toRouters(List<URL> urls) {
        List<Router> routers = new ArrayList<Router>();
        if (urls == null || urls.size() < 1) {
            return routers;
        }
        if (urls != null && urls.size() > 0) {
            for (URL url : urls) {
                if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
                    continue;
                }
                String routerType = url.getParameter(Constants.ROUTER_KEY);
                if (routerType != null && routerType.length() > 0) {
                    //設置url的protocol爲路由類型
                    url = url.setProtocol(routerType);
                }
                try {
                    //根據routerType獲取對應的路由器工廠,而後獲取具體路由器。而後放入路由器集合
		    //dubbo默認實現有file,script,condition三種類型工廠對應三種路由類型
                    //這些都是在RouterFactory$Adpative類完成適配的,RouterFactory$Adpative類是dubbo spi機制動態編碼,編譯生成的
                    Router router = routerFactory.getRouter(url);
                    if (!routers.contains(router))
                        routers.add(router);
                } catch (Throwable t) {
                    logger.error("convert router url to router error, url: " + url, t);
                }
            }
        }
        return routers;
    }

下一次,能夠說說,三種路由的具體實現和配置方法。編碼

相關文章
相關標籤/搜索