衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Dubbo通過RouterFactory接口實現路由機制服務

電子設計 ? 來源:博客園 ? 作者:佚名 ? 2020-04-16 07:43 ? 次閱讀

Dubbo路由機制是在服務間的調用時,通過將服務提供者按照設定的路由規則來決定調用哪一個具體的服務。

路由服務結構

Dubbo實現路由都是通過實現RouterFactory接口。當前版本 dubbo-2.7.5 實現該接口類如下:

路由實現工廠類是在 router 包下

由于 RouterFactory 是 SPI 接口,同時在獲取路由 RouterFactory#getRouter 方法上有 @Adaptive(“protocol”) 注解,所以在獲取路由的時候會動態調用需要的工廠類。

可以看到 getRouter 方法返回的是一個 Router 接口,該接口信息如下

其中 Router#route 是服務路由的入口,對于不同類型的路由工廠,有特定的 Router 實現類。

以上就是通過解析 URL,獲取到具體的 Router,通過調用 Router#router 過濾出符合當前路由規則的 invokers

服務路由實現

上面展示了路由實現類,這幾個實現類型中,ConditionRouter 條件路由是最為常用的類型,由于文章篇幅有限,本文就不對全部的路由類型逐一分析,只對條件路由進行具體分析,只要弄懂這一個類型,其它類型的解析就能容易掌握。

條件路由參數規則

在分析條件路由前,先了解條件路由的參數配置,官方文檔如下:

條件路由規則內容如下:

條件路由實現分析

分析路由實現,主要分析工廠類的 xxxRouterFactory#getRouter 和 xxxRouter#route 方法。

ConditionRouterFactory#getRouter

ConditionRouterFactory 中通過創建 ConditionRouter 對象來初始化解析相關參數配置。

在 ConditionRouter 構造函數中,從 URL 里獲取 rule 的字符串格式的規則,解析規則在 ConditionRouter#init 初始化方法中。

public void init(String rule) {

try {

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(“=》”);

// 消費者匹配條件

String whenRule = i 《 0 ? null : rule.substring(0, i).trim();

// 提供者地址匹配條件

String thenRule = i 《 0 ? rule.trim() : rule.substring(i + 2).trim();

// 解析消費者路由規則

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: 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);

}

}

以路由規則字符串中的=》為分隔符,將消費者匹配條件和提供者匹配條件分割,解析兩個路由規則后,賦值給當前對象的變量。

調用 parseRule 方法來解析消費者和服務者路由規則。

// 正則驗證路由規則

protected static final Pattern ROUTE_PATTERN = Pattern.compile(“([&!=,]*)\s*([^&!=,\s]+)”);

private static Map《String, MatchPair》 parseRule(String rule)

throws ParseException {

/**

* 條件變量和條件變量值的映射關系

* 比如 host =》 127.0.0.1 則保存著 host 和 127.0.0.1 的映射關系

*/

Map《String, MatchPair》 condition = new HashMap《String, MatchPair》();

if (StringUtils.isBlank(rule)) {

return condition;

}

// Key-Value pair, stores both match and mismatch conditions

MatchPair pair = null;

// Multiple values

Set《String》 values = null;

final Matcher matcher = ROUTE_PATTERN.matcher(rule);

while (matcher.find()) {

// 獲取正則前部分匹配(第一個括號)的內容

String separator = matcher.group(1);

// 獲取正則后部分匹配(第二個括號)的內容

String content = matcher.group(2);

// 如果獲取前部分為空,則表示規則開始位置,則當前 content 必為條件變量

if (StringUtils.isEmpty(separator)) {

pair = new MatchPair();

condition.put(content, pair);

}

// 如果分隔符是 &,則 content 為條件變量

else if (“&”.equals(separator)) {

// 當前 content 是條件變量,用來做映射集合的 key 的,如果沒有則添加一個元素

if (condition.get(content) == null) {

pair = new MatchPair();

condition.put(content, pair);

} else {

pair = condition.get(content);

}

}

// 如果當前分割符是 = ,則當前 content 為條件變量值

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());

}

// 由于 pair 還沒有被重新初始化,所以還是上一個條件變量的對象,所以可以將當前條件變量值在引用對象上賦值

values = pair.matches;

values.add(content);

}

// 如果當前分割符是 = ,則當前 content 也是條件變量值

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);

}

// 如果當前分割符為 ‘,’,則當前 content 也為條件變量值

else if (“,”.equals(separator)) { // Should be separated 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;

}

上面就是解析條件路由規則的過程,條件變量的值都保存在 MatchPair 中的 matches、mismatches 屬性中,=和,的條件變量值放在可以匹配的 matches 中,!=的條件變量值放在不可匹配路由規則的 mismatches 中。賦值過程中,代碼還是比較優雅。

實際上 matches、mismatches 就是保存的是條件變量值。

ConditionRouter#route

Router#route的作用就是匹配出符合路由規則的 Invoker 集合。

// 在初始化中進行被復制的變量

// 消費者條件匹配規則

protected Map《String, MatchPair》 whenCondition;

// 提供者條件匹配規則

protected Map《String, MatchPair》 thenCondition;

public 《T》 List《Invoker《T》》 route(List《Invoker《T》》 invokers, URL url, Invocation invocation)

throws RpcException {

if (!enabled) {

return invokers;

}

// 驗證 invokers 是否為空

if (CollectionUtils.isEmpty(invokers)) {

return invokers;

}

try {

// 校驗消費者是否有規則匹配,如果沒有則返回傳入的 Invoker

if (!matchWhen(url, invocation)) {

return invokers;

}

List《Invoker《T》》 result = new ArrayList《Invoker《T》》();

if (thenCondition == null) {

logger.warn(“The current consumer in the service blacklist. consumer: ” + NetUtils.getLocalHost() + “, service: ” + url.getServiceKey());

return result;

}

// 遍歷傳入的 invokers,匹配提供者是否有規則匹配

for (Invoker《T》 invoker : invokers) {

if (matchThen(invoker.getUrl(), url)) {

result.add(invoker);

}

}

// 如果 result 不為空,或當前對象 force=true 則返回 result 的 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(RULE_KEY));

return result;

}

} catch (Throwable t) {

logger.error(“Failed to execute condition router rule: ” + getUrl() + “, invokers: ” + invokers + “, cause: ” + t.getMessage(), t);

}

return invokers;

}

上面代碼可以看到,只要消費者沒有匹配的規則或提供者沒有匹配的規則及 force=false 時,不會返回傳入的參數的 Invoker。

匹配消費者路由規則和提供者路由規則方法是 matchWhen 和 matchThen

這兩個匹配方法都是調用同一個方法 matchCondition 實現的。將消費者或提供者 URL 轉為 Map,然后與 whenCondition 或 thenCondition 進行匹配。

匹配過程中,如果 key (即 sampleValue 值)存在對應的值,則通過 MatchPair#isMatch 方法再進行匹配。

private boolean isMatch(String value, URL param) {

// 存在可匹配的規則,不存在不可匹配的規則

if (!matches.isEmpty() && mismatches.isEmpty()) {

// 不可匹配的規則列表為空時,只要可匹配的規則匹配上,直接返回 true

for (String match : matches) {

if (UrlUtils.isMatchGlobPattern(match, value, param)) {

return true;

}

}

return false;

}

// 存在不可匹配的規則,不存在可匹配的規則

if (!mismatches.isEmpty() && matches.isEmpty()) {

// 不可匹配的規則列表中存在,則返回false

for (String mismatch : mismatches) {

if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {

return false;

}

}

return true;

}

// 存在可匹配的規則,也存在不可匹配的規則

if (!matches.isEmpty() && !mismatches.isEmpty()) {

// 都不為空時,不可匹配的規則列表中存在,則返回 false

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;

}

匹配過程再調用 UrlUtils#isMatchGlobPattern 實現

public static boolean isMatchGlobPattern(String pattern, String value, URL param) {

// 如果以 $ 開頭,則獲取 URL 中對應的值

if (param != null && pattern.startsWith(“$”)) {

pattern = param.getRawParameter(pattern.substring(1));

}

//

return isMatchGlobPattern(pattern, value);

}

public static boolean isMatchGlobPattern(String pattern, String value) {

if (“*”.equals(pattern)) {

return true;

}

if (StringUtils.isEmpty(pattern) && StringUtils.isEmpty(value)) {

return true;

}

if (StringUtils.isEmpty(pattern) || StringUtils.isEmpty(value)) {

return false;

}

// 獲取通配符位置

int i = pattern.lastIndexOf(‘*’);

// 如果value中沒有 “*” 通配符,則整個字符串值匹配

if (i == -1) {

return value.equals(pattern);

}

// 如果 “*” 在最后面,則匹配字符串 “*” 之前的字符串即可

else if (i == pattern.length() - 1) {

return value.startsWith(pattern.substring(0, i));

}

// 如果 “*” 在最前面,則匹配字符串 “*” 之后的字符串即可

else if (i == 0) {

return value.endsWith(pattern.substring(i + 1));

}

// 如果 “*” 不在字符串兩端,則同時匹配字符串 “*” 左右兩邊的字符串

else {

String prefix = pattern.substring(0, i);

String suffix = pattern.substring(i + 1);

return value.startsWith(prefix) && value.endsWith(suffix);

}

}

就這樣完成全部的條件路由規則匹配,雖然看似代碼較為繁雜,但是理清規則、思路,一步一步還是較好解析,前提是要熟悉相關參數的用法及形式,不然代碼較難理解。

最后

單純從邏輯上,如果能夠掌握條件路由的實現,去研究其它方式的路由實現,相信不會有太大問題。只是例如像腳本路由的實現,你得先會使用腳本執行引擎為前提,不然就不理解它的代碼。最后,在 dubbo-admin 上可以設置路由,大家可以嘗試各種使用規則,通過實操才能更好掌握和理解路由機制的實現。

責任編輯:gt


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8691

    瀏覽量

    151918
  • 路由
    +關注

    關注

    0

    文章

    278

    瀏覽量

    41931
收藏 人收藏

    評論

    相關推薦

    聊聊Dubbo - Dubbo可擴展機制實戰

    的做到了上面兩點。這要得益于Dubbo的微內核+插件的機制。接下來的章節中我們會慢慢揭開Dubbo擴展機制的神秘面紗。2. 可擴展的幾種解決方案通常可擴展的
    發表于 06-04 17:33

    聊聊Dubbo - Dubbo可擴展機制源碼解析

    摘要: 在Dubbo可擴展機制實戰中,我們了解了Dubbo擴展機制的一些概念,初探了Dubbo中LoadBalance的
    發表于 06-05 18:43

    Dubbo開源現狀與未來規劃

    循環依賴和反向依賴的出現。Dubbo 還有一個很重要的設計哲學就是平等對待第三方的擴展,即 Dubbo 內建的功能也是通過同樣的擴展機制提供出來的,第三方的擴展和內建功能可以相互取代。
    發表于 07-05 15:21

    攜程的 Dubbo 之路

    用戶自行擴展,我們在PB序列化器的實現上增加了擴展接口,允許用戶在外圍繼續增加數據壓縮的功能。整體序列化器的實現并不是很難,倒是有一點需要注意的是,由于 Dubbo
    發表于 10-12 15:05

    基于FPGA的VGA接口實現和字符顯示

    基于FPGA的VGA接口實現和字符顯示論文
    發表于 10-29 17:18 ?8次下載

    如何通過STM32的串口實現簡易脫機編程器

    如何通過STM32的串口實現簡易脫機編程器如何通過STM32的串口實現簡易脫機編程器如何通過STM32的串
    發表于 04-25 09:38 ?60次下載

    Dubbo源代碼實現服務調用的動態代理和負載均衡

    我們知道,Dubbo服務調用封裝成普通的Spring的Bean,于是我們可以像使用本地的Spring Bean一樣,來調用遠端的Dubbo服務,并有LoadBalance和Failo
    發表于 03-12 14:35 ?0次下載

    多態路由機制研究

    如何基于有限且確定的路由結構來支持多樣化服務是當前研究面臨的問題,采用路由結構的自組織和自調節來實現路由與業務的自適配,提出一種面向多樣化
    發表于 03-13 16:43 ?2次下載
    多態<b class='flag-5'>路由</b><b class='flag-5'>機制</b>研究

    服務化改造實踐(一)| Dubbo + ZooKeeper

    ?GreetingService?{String?sayHello(String?name);}2、服務端:服務實現實現 ?GreetingService接口,并通過 @Service
    發表于 08-27 16:36 ?247次閱讀
    <b class='flag-5'>服務</b>化改造實踐(一)| <b class='flag-5'>Dubbo</b> + ZooKeeper

    服務化改造實踐(一)| Dubbo + ZooKeeper

    GreetingService接口,并通過 @Service 來標注其為Dubbo的一個服務。@Servicepublic?class?AnnotatedGreetingServic
    發表于 08-27 17:25 ?319次閱讀
    <b class='flag-5'>服務</b>化改造實踐(一)| <b class='flag-5'>Dubbo</b> + ZooKeeper

    dubbo-go 中的 TPS Limit 設計與實現

    限流,比如說限制一分鐘內某個接口只能訪問 200 次,超過這個次數,則會被拒絕服務。在 Dubbo 的 Java 版本上,只有一個實現,就是 DefaultTPSLimiter 。 D
    發表于 03-17 15:27 ?658次閱讀

    Dubbo 如何成為連接異構微服務體系的最佳服務開發框架

    從編程開發的角度來說,Apache Dubbo (以下簡稱 Dubbo )首先是一款 RPC 服務框架,它最大的優勢在于提供了面向接口代理的服務
    發表于 03-12 17:04 ?965次閱讀
    <b class='flag-5'>Dubbo</b> 如何成為連接異構微<b class='flag-5'>服務</b>體系的最佳<b class='flag-5'>服務</b>開發框架

    ARM與FPGA的接口實現的解析

    ARM與FPGA的接口實現的解析(應廣單片機)-該文檔為ARM與FPGA的接口實現的解析詳述資料,講解的還不錯,感興趣的可以下載看看…………………………
    發表于 07-22 09:47 ?14次下載
    ARM與FPGA的<b class='flag-5'>接口實現</b>的解析

    C#-Interface接口實現

    C#-Interface接口實現(安徽理士電源技術有限公司招聘信息)-該文檔為C#-Interface接口實現講解文檔,是一份還算不錯的參考文檔,感興趣的可以下載看看,,,,,,,,,,,,,,,,,,
    發表于 09-28 09:42 ?3次下載
    C#-Interface<b class='flag-5'>接口實現</b>

    通過標準的CAN接口實現的測試臺自動化解決方案

    通過標準的CAN接口實現的測試臺自動化解決方案
    的頭像 發表于 10-11 14:17 ?1892次閱讀
    百家乐单机游戏下| 百家乐书包| 江北区| 华侨人百家乐的玩法技巧和规则| 百家乐官网伴侣破解版| 德州扑克技巧| 郑州百家乐的玩法技巧和规则 | 石狮市| bet365滚球| 威尼斯人娱乐城投注网| 百家乐去哪里玩最好| 百家乐官网增值公式| 海南博彩业| 百家乐专用| 百家乐哪家赌安全| 新濠峰百家乐官网的玩法技巧和规则 | 百家乐视频软件下载| 百家乐官网赌法| 汝南县| 大发888游戏平台88| 百家乐筹码价格| 百家乐游戏平台架设| 美女百家乐官网的玩法技巧和规则| 百家乐官网室系统软件| 永利博线上娱乐| 免费百家乐计划工具| 网上百家乐怎么赌能赢钱| 网上百家乐如何作假| 时时博百家乐官网的玩法技巧和规则 | 百家乐扑克多少张| 视频百家乐赌法| 百家乐五湖四海赌场娱乐网规则| 百家乐方案| 定制百家乐桌子| 大发888娱乐场下载ypu rd| 十六浦娱乐城官网| 绥德县| 太阳城百家乐官网如何看路| 百家乐官网最低下注| 百家乐官网庄闲的比例| 网页百家乐官网的玩法技巧和规则|