摘要:源碼分析一創建一個該類是基于條件表達式規則路由工廠類。路由工廠獲得配置項,默認為獲得獲得類型讀取規則獲得腳本路由獲得路由后記該部分相關的源碼解析地址該文章講解了集群中關于路由規則實現的部分。
集群——router
目標:介紹dubbo中集群的路由,介紹dubbo-cluster下router包的源碼。前言
路由規則 決定一次 dubbo 服務調用的目標服務器,分為條件路由規則和腳本路由規則,并且支持可擴展 。
源碼分析 (一)ConditionRouterFactorypublic class ConditionRouterFactory implements RouterFactory { public static final String NAME = "condition"; @Override public Router getRouter(URL url) { // 創建一個ConditionRouter return new ConditionRouter(url); } }
該類是基于條件表達式規則路由工廠類。
(二)ConditionRouter該類是基于條件表達式的路由實現類。關于給予條件表達式的路由規則,可以查看官方文檔:
官方文檔地址:http://dubbo.apache.org/zh-cn...1.屬性
private static final Logger logger = LoggerFactory.getLogger(ConditionRouter.class); /** * 分組正則匹配 */ private static Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)s*([^&!=,s]+)"); /** * 路由規則 URL */ private final URL url; /** * 路由規則的優先級,用于排序,優先級越大越靠前執行,可不填,缺省為 0 */ private final int priority; /** * 當路由結果為空時,是否強制執行,如果不強制執行,路由結果為空的路由規則將自動失效,可不填,缺省為 false 。 */ private final boolean force; /** * 消費者匹配條件集合,通過解析【條件表達式 rule 的 `=>` 之前半部分】 */ private final Map2.構造方法whenCondition; /** * 提供者地址列表的過濾條件,通過解析【條件表達式 rule 的 `=>` 之后半部分】 */ private final Map thenCondition;
public ConditionRouter(URL url) { this.url = url; // 獲得優先級配置 this.priority = url.getParameter(Constants.PRIORITY_KEY, 0); // 獲得是否強制執行配置 this.force = url.getParameter(Constants.FORCE_KEY, false); try { // 獲得規則 String rule = url.getParameterAndDecoded(Constants.RULE_KEY); if (rule == null || rule.trim().length() == 0) { throw new IllegalArgumentException("Illegal route rule!"); } rule = rule.replace("consumer.", "").replace("provider.", ""); int i = rule.indexOf("=>"); // 分割消費者和提供者規則 String whenRule = i < 0 ? null : rule.substring(0, i).trim(); String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim(); Map3.MatchPairwhen = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap () : parseRule(whenRule); Map then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule); // NOTE: It should be determined on the business level whether the `When condition` can be empty or not. this.whenCondition = when; this.thenCondition = then; } catch (ParseException e) { throw new IllegalStateException(e.getMessage(), e); } }
private static final class MatchPair { /** * 匹配的值的集合 */ final Setmatches = new HashSet (); /** * 不匹配的值的集合 */ final Set mismatches = new HashSet (); /** * 判斷value是否匹配matches或者mismatches * @param value * @param param * @return */ private boolean isMatch(String value, URL param) { // 只匹配 matches if (!matches.isEmpty() && mismatches.isEmpty()) { for (String match : matches) { if (UrlUtils.isMatchGlobPattern(match, value, param)) { // 匹配上了返回true return true; } } // 沒匹配上則為false return false; } // 只匹配 mismatches if (!mismatches.isEmpty() && matches.isEmpty()) { for (String mismatch : mismatches) { if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) { // 如果匹配上了,則返回false return false; } } // 沒匹配上,則為true return true; } // 匹配 matches和mismatches if (!matches.isEmpty() && !mismatches.isEmpty()) { //when both mismatches and matches contain the same value, then using mismatches first for (String mismatch : mismatches) { if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) { // 匹配上則為false return false; } } for (String match : matches) { if (UrlUtils.isMatchGlobPattern(match, value, param)) { // 匹配上則為true return true; } } return false; } return false; } }
該類是內部類,封裝了匹配的值,每個屬性條件。并且提供了判斷是否匹配的方法。
4.parseRuleprivate static MapparseRule(String rule) throws ParseException { Map condition = new HashMap (); // 如果規則為空,則直接返回空 if (StringUtils.isBlank(rule)) { return condition; } // Key-Value pair, stores both match and mismatch conditions MatchPair pair = null; // Multiple values Set values = null; // 正則表達式匹配 final Matcher matcher = ROUTE_PATTERN.matcher(rule); // 一個一個匹配 while (matcher.find()) { // Try to match one by one String separator = matcher.group(1); String content = matcher.group(2); // Start part of the condition expression. // 開始條件表達式 if (separator == null || separator.length() == 0) { pair = new MatchPair(); // 保存條件 condition.put(content, pair); } // The KV part of the condition expression else if ("&".equals(separator)) { // 把參數的條件表達式放入condition if (condition.get(content) == null) { pair = new MatchPair(); condition.put(content, pair); } else { pair = condition.get(content); } } // The Value in the KV part. // 把值放入values 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); } // The Value in the KV part. // 把不等于的條件限制也放入values 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); } // The Value in the KV part, if Value have more than one items. // 如果以.分隔的也放入values else if (",".equals(separator)) { // Should be seperateed by "," if (values == null || values.isEmpty()) 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; }
該方法是根據規則解析路由配置內容。具體的可以參照官網的配置規則來解讀這里每一個分割取值作為條件的過程。
5.route@Override publicList > route(List > invokers, URL url, Invocation invocation) throws RpcException { // 為空,直接返回空 Invoker 集合 if (invokers == null || invokers.isEmpty()) { return invokers; } try { // 如果不匹配 `whenCondition` ,直接返回 `invokers` 集合,因為不需要走 `whenThen` 的匹配 if (!matchWhen(url, invocation)) { return invokers; } List > result = new ArrayList >(); // 如果thenCondition為空,則直接返回空 if (thenCondition == null) { logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey()); return result; } // 遍歷invokers for (Invoker invoker : invokers) { // 如果thenCondition匹配,則加入result if (matchThen(invoker.getUrl(), url)) { result.add(invoker); } } if (!result.isEmpty()) { return result; } else if (force) { 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; }
該方法是進行路由規則的匹配,分別對消費者和提供者進行匹配。
6.matchConditionprivate boolean matchCondition(Mapcondition, URL url, URL param, Invocation invocation) { Map sample = url.toMap(); // 是否匹配 boolean result = false; // 遍歷條件 for (Map.Entry 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 { // sampleValue = sample.get(key); if (sampleValue == null) { sampleValue = sample.get(Constants.DEFAULT_KEY_PREFIX + key); } } if (sampleValue != null) { // 如果不匹配條件值,返回false if (!matchPair.getValue().isMatch(sampleValue, param)) { return false; } else { // 匹配則返回true result = true; } } else { //not pass the condition // 如果匹配的集合不為空 if (!matchPair.getValue().matches.isEmpty()) { // 返回false return false; } else { // 返回true result = true; } } } return result; }
該方法是匹配條件的主要邏輯。
(三)ScriptRouterFactory該類是基于腳本的路由規則工廠類。
public class ScriptRouterFactory implements RouterFactory { public static final String NAME = "script"; @Override public Router getRouter(URL url) { // 創建ScriptRouter return new ScriptRouter(url); } }(四)ScriptRouter
該類是基于腳本的路由實現類
1.屬性private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class); /** * 腳本類型 與 ScriptEngine 的映射緩存 */ private static final Map2.routeengines = new ConcurrentHashMap (); /** * 腳本 */ private final ScriptEngine engine; /** * 路由規則的優先級,用于排序,優先級越大越靠前執行,可不填,缺省為 0 。 */ private final int priority; /** * 路由規則 */ private final String rule; /** * 路由規則 URL */ private final URL url;
@Override @SuppressWarnings("unchecked") publicList > route(List > invokers, URL url, Invocation invocation) throws RpcException { try { List > invokersCopy = new ArrayList >(invokers); Compilable compilable = (Compilable) engine; // 創建腳本 Bindings bindings = engine.createBindings(); // 設置invokers、invocation、context bindings.put("invokers", invokersCopy); bindings.put("invocation", invocation); bindings.put("context", RpcContext.getContext()); // 編譯腳本 CompiledScript function = compilable.compile(rule); // 執行腳本 Object obj = function.eval(bindings); // 根據結果類型,轉換成 (List > 類型返回 if (obj instanceof Invoker[]) { invokersCopy = Arrays.asList((Invoker []) obj); } else if (obj instanceof Object[]) { invokersCopy = new ArrayList >(); for (Object inv : (Object[]) obj) { invokersCopy.add((Invoker ) inv); } } else { invokersCopy = (List >) obj; } return invokersCopy; } catch (ScriptException e) { //fail then ignore rule .invokers. // 發生異常,忽略路由規則,返回全 `invokers` 集合 logger.error("route error , rule has been ignored. rule: " + rule + ", method:" + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e); return invokers; } }
該方法是根據路由規則選擇invoker的實現邏輯。
(五)FileRouterFactory該類是裝飾者,對RouterFactory進行了功能增強,增加了從文件中讀取規則。
public class FileRouterFactory implements RouterFactory { public static final String NAME = "file"; /** * 路由工廠 */ private RouterFactory routerFactory; public void setRouterFactory(RouterFactory routerFactory) { this.routerFactory = routerFactory; } @Override public Router getRouter(URL url) { try { // Transform File URL into Script Route URL, and Load // file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=后記// 獲得 router 配置項,默認為 script String protocol = url.getParameter(Constants.ROUTER_KEY, ScriptRouterFactory.NAME); // Replace original protocol (maybe "file") with "script" String type = null; // Use file suffix to config script type, e.g., js, groovy ... // 獲得path String path = url.getPath(); // 獲得類型 if (path != null) { int i = path.lastIndexOf("."); if (i > 0) { type = path.substring(i + 1); } } // 讀取規則 String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath()))); boolean runtime = url.getParameter(Constants.RUNTIME_KEY, false); // 獲得腳本路由url URL script = url.setProtocol(protocol).addParameter(Constants.TYPE_KEY, type).addParameter(Constants.RUNTIME_KEY, runtime).addParameterAndEncoded(Constants.RULE_KEY, rule); // 獲得路由 return routerFactory.getRouter(script); } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } } }
該部分相關的源碼解析地址:https://github.com/CrazyHZM/i...
該文章講解了集群中關于路由規則實現的部分。接下來我將開始對集群模塊關于Mock部分進行講解。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73255.html
摘要:服務引用過程目標從源碼的角度分析服務引用過程。并保留服務提供者的部分配置,比如版本,,時間戳等最后將合并后的配置設置為查詢字符串中。的可以參考源碼解析二十三遠程調用的一的源碼分析。 dubbo服務引用過程 目標:從源碼的角度分析服務引用過程。 前言 前面服務暴露過程的文章講解到,服務引用有兩種方式,一種就是直連,也就是直接指定服務的地址來進行引用,這種方式更多的時候被用來做服務測試,不...
摘要:大揭秘目標了解的新特性,以及版本升級的引導。四元數據改造我們知道以前的版本只有注冊中心,注冊中心的有數十個的鍵值對,包含了一個服務所有的元數據。 DUBBO——2.7大揭秘 目標:了解2.7的新特性,以及版本升級的引導。 前言 我們知道Dubbo在2011年開源,停止更新了一段時間。在2017 年 9 月 7 日,Dubbo 悄悄的在 GitHub 發布了 2.5.4 版本。隨后,版本...
摘要:可以參考源碼解析二十四遠程調用協議的八。十六的該類也是用了適配器模式,該類主要的作用就是增加了心跳功能,可以參考源碼解析十遠程通信層的四。二十的可以參考源碼解析十七遠程通信的一。 2.7大揭秘——消費端發送請求過程 目標:從源碼的角度分析一個服務方法調用經歷怎么樣的磨難以后到達服務端。 前言 前一篇文章講到的是引用服務的過程,引用服務無非就是創建出一個代理。供消費者調用服務的相關方法。...
摘要:源碼分析一創建該類是服務降級的裝飾器類,對進行了功能增強,增強了服務降級的功能。注意隱式契約盡管描述被添加到接口聲明中,但是可擴展性是一個問題。獲得服務類型獲得創建加入集合該方法是獲得。 集群——Mock 目標:介紹dubbo中集群的Mock,介紹dubbo-cluster下關于服務降級和本地偽裝的源碼。 前言 本文講解兩塊內容,分別是本地偽裝和服務降級,本地偽裝通常用于服務降級,比如...
摘要:大揭秘異步化改造目標從源碼的角度分析的新特性中對于異步化的改造原理。看源碼解析四十六消費端發送請求過程講到的十四的,在以前的邏輯會直接在方法中根據配置區分同步異步單向調用。改為關于可以參考源碼解析十遠程通信層的六。 2.7大揭秘——異步化改造 目標:從源碼的角度分析2.7的新特性中對于異步化的改造原理。 前言 dubbo中提供了很多類型的協議,關于協議的系列可以查看下面的文章: du...
閱讀 1111·2021-09-22 16:04
閱讀 1494·2019-08-30 15:43
閱讀 1097·2019-08-29 14:01
閱讀 3438·2019-08-26 12:19
閱讀 3353·2019-08-26 12:15
閱讀 1444·2019-08-26 12:13
閱讀 3264·2019-08-23 17:00
閱讀 1483·2019-08-23 15:38