這篇說說,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); }