摘要:源碼分析一創(chuàng)建一個(gè)該類是基于條件表達(dá)式規(guī)則路由工廠類。路由工廠獲得配置項(xiàng),默認(rèn)為獲得獲得類型讀取規(guī)則獲得腳本路由獲得路由后記該部分相關(guān)的源碼解析地址該文章講解了集群中關(guān)于路由規(guī)則實(shí)現(xiàn)的部分。
集群——router
目標(biāo):介紹dubbo中集群的路由,介紹dubbo-cluster下router包的源碼。前言
路由規(guī)則 決定一次 dubbo 服務(wù)調(diào)用的目標(biāo)服務(wù)器,分為條件路由規(guī)則和腳本路由規(guī)則,并且支持可擴(kuò)展 。
源碼分析 (一)ConditionRouterFactorypublic class ConditionRouterFactory implements RouterFactory { public static final String NAME = "condition"; @Override public Router getRouter(URL url) { // 創(chuàng)建一個(gè)ConditionRouter return new ConditionRouter(url); } }
該類是基于條件表達(dá)式規(guī)則路由工廠類。
(二)ConditionRouter該類是基于條件表達(dá)式的路由實(shí)現(xiàn)類。關(guān)于給予條件表達(dá)式的路由規(guī)則,可以查看官方文檔:
官方文檔地址: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]+)"); /** * 路由規(guī)則 URL */ private final URL url; /** * 路由規(guī)則的優(yōu)先級(jí),用于排序,優(yōu)先級(jí)越大越靠前執(zhí)行,可不填,缺省為 0 */ private final int priority; /** * 當(dāng)路由結(jié)果為空時(shí),是否強(qiáng)制執(zhí)行,如果不強(qiáng)制執(zhí)行,路由結(jié)果為空的路由規(guī)則將自動(dòng)失效,可不填,缺省為 false 。 */ private final boolean force; /** * 消費(fèi)者匹配條件集合,通過解析【條件表達(dá)式 rule 的 `=>` 之前半部分】 */ private final Map2.構(gòu)造方法whenCondition; /** * 提供者地址列表的過濾條件,通過解析【條件表達(dá)式 rule 的 `=>` 之后半部分】 */ private final Map thenCondition;
public ConditionRouter(URL url) { this.url = url; // 獲得優(yōu)先級(jí)配置 this.priority = url.getParameter(Constants.PRIORITY_KEY, 0); // 獲得是否強(qiáng)制執(zhí)行配置 this.force = url.getParameter(Constants.FORCE_KEY, false); try { // 獲得規(guī)則 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("=>"); // 分割消費(fèi)者和提供者規(guī)則 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; } }
該類是內(nèi)部類,封裝了匹配的值,每個(gè)屬性條件。并且提供了判斷是否匹配的方法。
4.parseRuleprivate static MapparseRule(String rule) throws ParseException { Map condition = new HashMap (); // 如果規(guī)則為空,則直接返回空 if (StringUtils.isBlank(rule)) { return condition; } // Key-Value pair, stores both match and mismatch conditions MatchPair pair = null; // Multiple values Set values = null; // 正則表達(dá)式匹配 final Matcher matcher = ROUTE_PATTERN.matcher(rule); // 一個(gè)一個(gè)匹配 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. // 開始條件表達(dá)式 if (separator == null || separator.length() == 0) { pair = new MatchPair(); // 保存條件 condition.put(content, pair); } // The KV part of the condition expression else if ("&".equals(separator)) { // 把參數(shù)的條件表達(dá)式放入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; }
該方法是根據(jù)規(guī)則解析路由配置內(nèi)容。具體的可以參照官網(wǎng)的配置規(guī)則來解讀這里每一個(gè)分割取值作為條件的過程。
5.route@Override publicList > route(List > invokers, URL url, Invocation invocation) throws RpcException { // 為空,直接返回空 Invoker 集合 if (invokers == null || invokers.isEmpty()) { return invokers; } try { // 如果不匹配 `whenCondition` ,直接返回 `invokers` 集合,因?yàn)椴恍枰?`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; }
該方法是進(jìn)行路由規(guī)則的匹配,分別對(duì)消費(fèi)者和提供者進(jìn)行匹配。
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該類是基于腳本的路由規(guī)則工廠類。
public class ScriptRouterFactory implements RouterFactory { public static final String NAME = "script"; @Override public Router getRouter(URL url) { // 創(chuàng)建ScriptRouter return new ScriptRouter(url); } }(四)ScriptRouter
該類是基于腳本的路由實(shí)現(xiàn)類
1.屬性private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class); /** * 腳本類型 與 ScriptEngine 的映射緩存 */ private static final Map2.routeengines = new ConcurrentHashMap (); /** * 腳本 */ private final ScriptEngine engine; /** * 路由規(guī)則的優(yōu)先級(jí),用于排序,優(yōu)先級(jí)越大越靠前執(zhí)行,可不填,缺省為 0 。 */ private final int priority; /** * 路由規(guī)則 */ private final String rule; /** * 路由規(guī)則 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; // 創(chuàng)建腳本 Bindings bindings = engine.createBindings(); // 設(shè)置invokers、invocation、context bindings.put("invokers", invokersCopy); bindings.put("invocation", invocation); bindings.put("context", RpcContext.getContext()); // 編譯腳本 CompiledScript function = compilable.compile(rule); // 執(zhí)行腳本 Object obj = function.eval(bindings); // 根據(jù)結(jié)果類型,轉(zhuǎn)換成 (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. // 發(fā)生異常,忽略路由規(guī)則,返回全 `invokers` 集合 logger.error("route error , rule has been ignored. rule: " + rule + ", method:" + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e); return invokers; } }
該方法是根據(jù)路由規(guī)則選擇invoker的實(shí)現(xiàn)邏輯。
(五)FileRouterFactory該類是裝飾者,對(duì)RouterFactory進(jìn)行了功能增強(qiáng),增加了從文件中讀取規(guī)則。
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 配置項(xiàng),默認(rèn)為 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); } } // 讀取規(guī)則 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); } } }
該部分相關(guān)的源碼解析地址:https://github.com/CrazyHZM/i...
該文章講解了集群中關(guān)于路由規(guī)則實(shí)現(xiàn)的部分。接下來我將開始對(duì)集群模塊關(guān)于Mock部分進(jìn)行講解。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/73255.html
摘要:服務(wù)引用過程目標(biāo)從源碼的角度分析服務(wù)引用過程。并保留服務(wù)提供者的部分配置,比如版本,,時(shí)間戳等最后將合并后的配置設(shè)置為查詢字符串中。的可以參考源碼解析二十三遠(yuǎn)程調(diào)用的一的源碼分析。 dubbo服務(wù)引用過程 目標(biāo):從源碼的角度分析服務(wù)引用過程。 前言 前面服務(wù)暴露過程的文章講解到,服務(wù)引用有兩種方式,一種就是直連,也就是直接指定服務(wù)的地址來進(jìn)行引用,這種方式更多的時(shí)候被用來做服務(wù)測試,不...
摘要:大揭秘目標(biāo)了解的新特性,以及版本升級(jí)的引導(dǎo)。四元數(shù)據(jù)改造我們知道以前的版本只有注冊(cè)中心,注冊(cè)中心的有數(shù)十個(gè)的鍵值對(duì),包含了一個(gè)服務(wù)所有的元數(shù)據(jù)。 DUBBO——2.7大揭秘 目標(biāo):了解2.7的新特性,以及版本升級(jí)的引導(dǎo)。 前言 我們知道Dubbo在2011年開源,停止更新了一段時(shí)間。在2017 年 9 月 7 日,Dubbo 悄悄的在 GitHub 發(fā)布了 2.5.4 版本。隨后,版本...
摘要:可以參考源碼解析二十四遠(yuǎn)程調(diào)用協(xié)議的八。十六的該類也是用了適配器模式,該類主要的作用就是增加了心跳功能,可以參考源碼解析十遠(yuǎn)程通信層的四。二十的可以參考源碼解析十七遠(yuǎn)程通信的一。 2.7大揭秘——消費(fèi)端發(fā)送請(qǐng)求過程 目標(biāo):從源碼的角度分析一個(gè)服務(wù)方法調(diào)用經(jīng)歷怎么樣的磨難以后到達(dá)服務(wù)端。 前言 前一篇文章講到的是引用服務(wù)的過程,引用服務(wù)無非就是創(chuàng)建出一個(gè)代理。供消費(fèi)者調(diào)用服務(wù)的相關(guān)方法。...
摘要:源碼分析一創(chuàng)建該類是服務(wù)降級(jí)的裝飾器類,對(duì)進(jìn)行了功能增強(qiáng),增強(qiáng)了服務(wù)降級(jí)的功能。注意隱式契約盡管描述被添加到接口聲明中,但是可擴(kuò)展性是一個(gè)問題。獲得服務(wù)類型獲得創(chuàng)建加入集合該方法是獲得。 集群——Mock 目標(biāo):介紹dubbo中集群的Mock,介紹dubbo-cluster下關(guān)于服務(wù)降級(jí)和本地偽裝的源碼。 前言 本文講解兩塊內(nèi)容,分別是本地偽裝和服務(wù)降級(jí),本地偽裝通常用于服務(wù)降級(jí),比如...
摘要:大揭秘異步化改造目標(biāo)從源碼的角度分析的新特性中對(duì)于異步化的改造原理??丛创a解析四十六消費(fèi)端發(fā)送請(qǐng)求過程講到的十四的,在以前的邏輯會(huì)直接在方法中根據(jù)配置區(qū)分同步異步單向調(diào)用。改為關(guān)于可以參考源碼解析十遠(yuǎn)程通信層的六。 2.7大揭秘——異步化改造 目標(biāo):從源碼的角度分析2.7的新特性中對(duì)于異步化的改造原理。 前言 dubbo中提供了很多類型的協(xié)議,關(guān)于協(xié)議的系列可以查看下面的文章: du...
閱讀 1121·2021-09-22 16:04
閱讀 1499·2019-08-30 15:43
閱讀 1109·2019-08-29 14:01
閱讀 3444·2019-08-26 12:19
閱讀 3359·2019-08-26 12:15
閱讀 1452·2019-08-26 12:13
閱讀 3270·2019-08-23 17:00
閱讀 1490·2019-08-23 15:38