dubbo路由代碼分析3(condition路由器)

這篇說說,dubbo condition類型路由器的路由解析和執行過程
http://www.javashuo.com/article/p-gnjacrbl-du.html這篇咱們能夠看到
condition類型路由器是由condition路由工廠獲取,源碼以下java

public class ConditionRouterFactory implements RouterFactory {

    public static final String NAME = "condition";
    //返回ConditionRouter類型路由器
    public Router getRouter(URL url) {
        return new ConditionRouter(url);
    }

}

ConditionRouterFactory是RouterFactory 經過spi機制的一種實現。具體看下,condition路由器的源碼,這裏先貼出兩個個方法,一個構造方法,一個是路由方法正則表達式

/**
 * ConditionRouter 類生命
 * 實現了Comparable接口,是爲了路由排序用的
 * @author william.liangf
 */
public class ConditionRouter implements Router, Comparable<Router>{}

屬性數據結構

private static final Logger logger = LoggerFactory.getLogger(ConditionRouter.class);
    private static Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)");
    //路由器的信息來源:url
    private final URL url;
    //路由器優先級,在多個路由排序用的
    private final int priority;
    //是否強制執行路由規則,哪怕沒有合適的invoker
    private final boolean force;
    //存放consumer路由規則
    private final Map<String, MatchPair> whenCondition;
    //存放provider路由規則
    private final Map<String, MatchPair> thenCondition;
/***
     * pulibc 構造函數,爲各個屬性賦值
     * @param url
     */
    public ConditionRouter(URL url) {
        this.url = url;
        this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
        this.force = url.getParameter(Constants.FORCE_KEY, false);
        try {
            //經過rule key獲取路由規則字串
            String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
            if (rule == null || rule.trim().length() == 0) {
                throw new IllegalArgumentException("Illegal route rule!");
            }
            //把字符串裏的"consumer." "provider." 替換掉,方便解析
            rule = rule.replace("consumer.", "").replace("provider.", "");
            int i = rule.indexOf("=>");
            //以"=>"爲分割線,前面是consumer規則,後面是provider 規則
            String whenRule = i < 0 ? null : rule.substring(0, i).trim();
            String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim();
            //parseRule 方法解析規則,放在Map<String, MatchPair>裏
            Map<String, MatchPair> when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<String, MatchPair>() : parseRule(whenRule);
            Map<String, MatchPair> then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule);
            // NOTE: When條件是容許爲空的,外部業務來保證相似的約束條件
            //解析構造的規則放在condition變量裏
            this.whenCondition = when;
            this.thenCondition = then;
        } catch (ParseException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    //代碼略...
       /***
     * 路由方法,執行路由規則
     * @param invokers
     * @param url        refer url
     * @param invocation
     * @param <T>
     * @return
     * @throws RpcException
     */
    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
            throws RpcException {
        if (invokers == null || invokers.size() == 0) {
            return invokers;
        }
        try {
            //前置條件不匹配,說明consumer不在限制之列。說明,路由不針對當前客戶,這樣就所有放行,全部提供者均可以調用。
            //這是consumer的url
            if (!matchWhen(url, invocation)) {
                return invokers;
            }
            List<Invoker<T>> result = new ArrayList<Invoker<T>>();
            //thenCondition爲null表示拒絕一切請求
            if (thenCondition == null) {
                logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
                return result;
            }
            for (Invoker<T> invoker : invokers) {
                //調用放匹配才放行。服務提供者,只要符合路由才,能提供服務,這裏的url改成invoker.getUrl()
                //都不匹配,這裏result多是空的。
                if (matchThen(invoker.getUrl(), url)) {
                    result.add(invoker);
                }
            }
            if (result.size() > 0) {
                return result;
            } else if (force) {
                //force強制執行路由。哪怕result是空的,也要返回給上層方法。若是爲false,最後放回全部的invokers,等於不執行路由
                logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(Constants.RULE_KEY));
                return result;
            }
        } catch (Throwable t) {
            logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
        }
        return invokers;
    }

 總的來講,構造函數,初始化路由規則。路由方法,根據路由規則對,調用方(一個)和服務提供方(多個)執行路由規則。
 讓符合規則的調用方,能夠調用,
 讓不符合規則的調用方不能調用。
 讓符合規則的服務提供方,留着服務提供者列表。
 讓不符合路由規則的服務提供方,從服務者列表中除去。ide

 先看下,存放路由規則的數據結構。也就是下面量個屬性
     //存放consumer路由規則
    private final Map<String, MatchPair> whenCondition;
    //存放provider路由規則
    private final Map<String, MatchPair> thenCondition;
 我以,以下的路由規則爲例函數

"host !=4.4.4.4 & host = 2.2.2.2,1.1.1.1,3.3.3.3 &method =sayHello => host = 1.2.3.4&host !=4.4.4.4"


 看到通過規則解析後,存放在上面兩個對象的內容以下圖:this

 能夠看到,路由條件,可分爲host和method的兩類。
 每一類,又可分爲容許類(match)和不容許(diamatch)兩類規則,
 每一類規則有,能夠包含多條路由信息。 路由規則存放,經過,Map結合MatchPair數據結構完成。
MatchPair結構以下:url

/***
     * MatchPiar 數據結構,它包含類具體匹配方法isMatch
     * 是個私有,靜態類,爲何靜態??
     */
    private static final class MatchPair {
        //容許列表,內部用Set集合類型,防止重複。
        final Set<String> matches = new HashSet<String>();
        //拒絕規則
        final Set<String> mismatches = new HashSet<String>();
        //具體執行匹配規則的方法
        private boolean isMatch(String value, URL param) {
            //只有容許項目
            if (matches.size() > 0 && mismatches.size() == 0) {
                for (String match : matches) {
                    //value只要知足一項,容許,就爲匹配,經過
                    if (UrlUtils.isMatchGlobPattern(match, value, param)) {
                        return true;
                    }
                }
                return false;
            }
              //只有不容許項目
            if (mismatches.size() > 0 && matches.size() == 0) {
                //value是要知足一項,不容許。就是不匹配
                for (String mismatch : mismatches) {
                    if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
                        return false;
                    }
                }
                //必須所有不知足不匹配。才經過
                return true;
            }

            if (matches.size() > 0 && mismatches.size() > 0) {
                //when both mismatches and matches contain the same value, then using mismatches first
                //當匹配項和不匹配項包含一樣的值,不匹配項優先
                for (String mismatch : mismatches) {
                    if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
                        return false;
                    }
                }
                for (String match : matches) {
                    if (UrlUtils.isMatchGlobPattern(match, value, param)) {
                        return true;
                    }
                }
                return false;
            }
            return false;
        }
    }

路由規則的解析,由parseRule方法完成。把規則字符串放到condition對象裏。.net

/**
     * 解析規則字串解析後放到map裏
     * 這裏有個數據結構類MatchPair 用set放着,容許規則matches,和不容許規則mismatches,他們都是set結構
     * @param rule
     * @return
     * @throws ParseException
     */
    private static Map<String, MatchPair> parseRule(String rule)
            throws ParseException {
        Map<String, MatchPair> condition = new HashMap<String, MatchPair>();
        if (StringUtils.isBlank(rule)) {
            return condition;
        }
        // 匹配或不匹配Key-Value對
        MatchPair pair = null;
        // 多個Value值
        Set<String> values = null;
        //用java的正則表達式Pattern的matcher去分割字符串到Matcher類,而後逐個匹配。
        //這個要熟悉正則,和Matcher,Pattern
        final Matcher matcher = ROUTE_PATTERN.matcher(rule);
        while (matcher.find()) { //逐個匹配
            String separator = matcher.group(1);
            String content = matcher.group(2);
            // 表達式開始
            if (separator == null || separator.length() == 0) {
                pair = new MatchPair();
                condition.put(content, pair);
            }
            // KV開始
            else if ("&".equals(separator)) {
                if (condition.get(content) == null) {
                    pair = new MatchPair();
                    condition.put(content, pair);
                } else {
                    pair = condition.get(content);
                }
            }
            // KV的Value部分開始
            else if ("=".equals(separator)) {
                if (pair == null)
                    throw new ParseException("Illegal route rule \""
                            + rule + "\", The error char '" + separator
                            + "' at index " + matcher.start() + " before \""
                            + content + "\".", matcher.start());

                values = pair.matches;
                values.add(content);
            }
            // KV的Value部分開始
            else if ("!=".equals(separator)) {
                if (pair == null)
                    throw new ParseException("Illegal route rule \""
                            + rule + "\", The error char '" + separator
                            + "' at index " + matcher.start() + " before \""
                            + content + "\".", matcher.start());

                values = pair.mismatches;
                values.add(content);
            }
            // KV的Value部分的多個條目
            else if (",".equals(separator)) { // 若是爲逗號表示
                if (values == null || values.size() == 0)
                    throw new ParseException("Illegal route rule \""
                            + rule + "\", The error char '" + separator
                            + "' at index " + matcher.start() + " before \""
                            + content + "\".", matcher.start());
                values.add(content);
            } else {
                throw new ParseException("Illegal route rule \"" + rule
                        + "\", The error char '" + separator + "' at index "
                        + matcher.start() + " before \"" + content + "\".", matcher.start());
            }
        }
        return condition;
    }

而後是,理由規則的執行。code

/***
     * 對服務調用方的匹配
     * @param url
     * @param invocation
     * @return
     */
    boolean matchWhen(URL url, Invocation invocation) {
        return whenCondition == null || whenCondition.isEmpty() || matchCondition(whenCondition, url, null, invocation);
    }

    /***
     * 對服務提供者的匹配
     * @param url
     * @param param
     * @return
     */
    private boolean matchThen(URL url, URL param) {
        return !(thenCondition == null || thenCondition.isEmpty()) && matchCondition(thenCondition, url, param, null);
    }

    /***
     * 具體執行匹配規則,遍歷condition裏全部的規則,執行規則。
     * @param condition
     * @param url
     * @param param
     * @param invocation
     * @return
     */
    private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param, Invocation invocation) {
        Map<String, String> sample = url.toMap();
        boolean result = false;
        for (Map.Entry<String, MatchPair> matchPair : condition.entrySet()) {
            String key = matchPair.getKey();
            String sampleValue;
            //get real invoked method name from invocation
            //路由規則支持到方法級別
            if (invocation != null && (Constants.METHOD_KEY.equals(key) || Constants.METHODS_KEY.equals(key))) {
                sampleValue = invocation.getMethodName();//獲取方法名
            } else {//key 是host  獲取host值
                sampleValue = sample.get(key);
            }
            if (sampleValue != null) {
                //調用MatchPair的isMatch方法
                if (!matchPair.getValue().isMatch(sampleValue, param)) {
                    return false;
                } else {
                    result = true;
                }
            } else {
                //not pass the condition
                //若是sampleValue沒有值,但匹配項有值。不經過
                if (matchPair.getValue().matches.size() > 0) {
                    return false;
                } else {
                    result = true;
                }
            }
        }
        return result;
    }

最後,是路由器的排序router

/***
     * 路由器的排序
     * @param o
     * @return
     */
    public int compareTo(Router o) {
        if (o == null || o.getClass() != ConditionRouter.class) {
            return 1;
        }
        ConditionRouter c = (ConditionRouter) o;
        //若是優先級相同,經過rul字符串比較
        return this.priority == c.priority ? url.toFullString().compareTo(c.url.toFullString()) : (this.priority > c.priority ? 1 : -1);
    }
相關文章
相關標籤/搜索