Dubbo 路由規(guī)則之標(biāo)簽路由

前言

大家好,今天開始給大家分享 — Dubbo 專題之 Dubbo 路由規(guī)則之標(biāo)簽路由爷耀。在前一個章節(jié)中我們介紹了 Dubbo 路由規(guī)則之標(biāo)簽路由,以及我們也例舉了常見的使用場景并且進(jìn)行了源碼解析來分析其實現(xiàn)原理拍皮,同時知道 Dubbo 中標(biāo)簽路由其本質(zhì)上是通過過濾器對服務(wù)提供者列表進(jìn)行規(guī)則的匹配歹叮,如果匹配不上則過濾掉服務(wù)提供者跑杭。那接下來我們解析討論標(biāo)簽路由,什么是標(biāo)簽路由呢盗胀?有什么使用場景呢艘蹋?下面就讓我們快速開始吧锄贼!

1. 標(biāo)簽路由簡介

首先小伙伴可以通過《Dubbo 路由規(guī)則之條件路由》回歸一下什么是路由規(guī)則票灰?下面我們主要討論什么標(biāo)簽路由:

標(biāo)簽路由

上圖中我們可以看到有兩個機(jī)房分別是機(jī)房A、機(jī)房B宅荤,其中機(jī)房 A 只能訪問到 Service A 和 Service B 屑迂,而機(jī)房B 只能訪問到 Service C 和 Service D。要實現(xiàn)上面這種場景我們就需要用到標(biāo)簽路由冯键。從機(jī)房 A 發(fā)起的調(diào)用攜帶標(biāo)簽 TAG_A 訪問到 Service A 和 Service B惹盼,而從機(jī)房 B 發(fā)起的調(diào)用攜帶 TAG_B Service C 和 Service D 。那什么是標(biāo)簽路由呢惫确?

  • 標(biāo)簽路由:以服務(wù)提供者應(yīng)用為粒度配置路由規(guī)則手报,通過將某一個或多個服務(wù)的提供者劃分到同一個分組,約束流量只在指定分組中流轉(zhuǎn)改化,從而實現(xiàn)流量隔離的目的掩蛤,可以作為藍(lán)綠發(fā)布、灰度發(fā)布等場景的能力基礎(chǔ)陈肛。標(biāo)簽主要是指對Provider端應(yīng)用實例的分組揍鸟,目前有兩種方式可以完成實例分組,分別是動態(tài)規(guī)則打標(biāo)靜態(tài)規(guī)則打標(biāo)句旱,其中動態(tài)規(guī)則相較于靜態(tài)規(guī)則優(yōu)先級更高阳藻,而當(dāng)兩種規(guī)則同時存在且出現(xiàn)沖突時,將以動態(tài)規(guī)則為準(zhǔn)谈撒。

2. 使用方式

下面我們簡單的討論下標(biāo)簽路由的使用方式:

2.1 標(biāo)簽路由

  • 動態(tài)規(guī)則打標(biāo)腥泥,可隨時在服務(wù)治理控制臺下發(fā)標(biāo)簽歸組規(guī)則
# demo-provider應(yīng)用增加了兩個標(biāo)簽分組tag1和tag2
  # tag1包含一個實例 127.0.0.1:20880
  # tag2包含一個實例 127.0.0.1:20881
  ---
    force: false
    runtime: true
    enabled: true
    key: demo-provider
    tags:
      - name: tag1
        addresses: ["127.0.0.1:20880"]
      - name: tag2
      addresses: ["127.0.0.1:20881"]
  • 靜態(tài)打標(biāo)
 <dubbo:provider tag="tag1"/>

或者

 <dubbo:service tag="tag1"/>

或者

 java -jar xxx-provider.jar -Ddubbo.provider.tag={the tag you want, may come from OS ENV}

Tips:消費端通過編程的方式使用RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY,"TAG_A")請求標(biāo)簽的作用域為每一次 invocation,使用 attachment 來傳遞請求標(biāo)簽啃匿,注意保存在 attachment 中的值將會在一次完整的遠(yuǎn)程調(diào)用中持續(xù)傳遞蛔外,得益于這樣的特性,我們只需要在起始調(diào)用時立宜,通過一行代碼的設(shè)置冒萄,達(dá)到標(biāo)簽的持續(xù)傳遞。

  • 字段說明:
編號 字段名稱 說明 必填
1 scope 路由規(guī)則的作用粒度橙数,scope的取值會決定key的取值尊流。<br />service 服務(wù)粒度 application 應(yīng)用粒度。 必填
2 Key 明確規(guī)則體作用在哪個接口服務(wù)或應(yīng)用灯帮。 scope=service時崖技,<br />key取值為[{group}:]{service}[:{version}]的組合 scope=application時逻住,<br />key取值為application名稱 。 必填
3 enabled enabled=true 當(dāng)前路由規(guī)則是否生效迎献,瞎访,缺省生效。 可不填
4 force force=false 當(dāng)路由結(jié)果為空時吁恍,是否強(qiáng)制執(zhí)行扒秸,如果不強(qiáng)制執(zhí)行,<br />路由結(jié)果為空的路由規(guī)則將自動失效冀瓦,缺省為 false伴奥。 可不填
5 runtime runtime=false 是否在每次調(diào)用時執(zhí)行路由規(guī)則,<br />否則只在提供者地址列表變更時預(yù)先執(zhí)行并緩存結(jié)果翼闽,<br />調(diào)用時直接從緩存中獲取路由結(jié)果拾徙。如果用了參數(shù)路由,必須設(shè)為 true感局,<br />需要注意設(shè)置會影響調(diào)用的性能尼啡,缺省為 false 可不填
6 priority priority=1 路由規(guī)則的優(yōu)先級询微,用于排序崖瞭,優(yōu)先級越大越靠前執(zhí)行,缺省為 0拓提。 可不填
7 tags 定義具體的標(biāo)簽分組內(nèi)容读恃,可定義任意n(n>=1)個標(biāo)簽并為每個標(biāo)簽指定實例列表。其中name為標(biāo)簽名稱 必填

2.2 降級約定

  • request.tag=tag1 時優(yōu)先選擇標(biāo)記了tag=tag1provider代态。若集群中不存在與請求標(biāo)記對應(yīng)的服務(wù)寺惫,默認(rèn)將降級請求 tag為空的provider;如果要改變這種默認(rèn)行為蹦疑,即找不到匹配tag1provider返回異常西雀,需設(shè)置request.tag.force=true
  • request.tag未設(shè)置時歉摧,只會匹配tag為空的provider艇肴。即使集群中存在可用的服務(wù),若 tag 不匹配也就無法調(diào)用叁温,這與約定1不同再悼,攜帶標(biāo)簽的請求可以降級訪問到無標(biāo)簽的服務(wù),但不攜帶標(biāo)簽/攜帶其他種類標(biāo)簽的請求永遠(yuǎn)無法訪問到其他標(biāo)簽的服務(wù)膝但。

Tips: 2.6.x 版本以及更早的版本請使用老版本路由規(guī)則冲九,自定義路由參考路由擴(kuò)展

3. 使用場景

從上面的簡單介紹我們可以大致了解到,標(biāo)簽路由通過將某一個或多個服務(wù)的提供者劃分到同一個分組跟束,約束流量只在指定分組中流轉(zhuǎn)莺奸,從而實現(xiàn)流量隔離的目的丑孩。我們?nèi)粘9ぷ髦谐S玫膱鼍坝校核{(lán)綠發(fā)布、灰度發(fā)布等場景的能力基礎(chǔ)等灭贷。

4. 示例演示

我們以獲取圖書列表為例進(jìn)行實例演示温学,其中我們會啟動兩個服務(wù)提供者配置兩個端口:2088020881甚疟,然后分別指定兩個服務(wù)標(biāo)簽為:TAG_A仗岖、TAG_B。項目結(jié)構(gòu)圖如下:

idea

這里我們使用動態(tài)打標(biāo)的方式所有 XML 中的配置維持以前案例的配置古拴,我們主要看看 Dubbo Admin 中的配置:

idea1
# demo-provider 應(yīng)用增加了兩個標(biāo)簽分組 TAG_A 和 TAG_B
# TAG_A 包含一個實例 127.0.0.1:20880
# TAG_B 包含一個實例 127.0.0.1:20881
force: true
enabled: true
runtime: false
tags:
 - name: TAG_A
   addresses: [192.168.0.1:20880]
 - name: TAG_B
   addresses: [192.168.0.2:20881]

以上動態(tài)打標(biāo)配置表示:當(dāng)消費端指定標(biāo)簽為 TAG_A 時調(diào)用 127.0.0.1:20880 服務(wù)提供者箩帚,標(biāo)簽為 TAG_B 時調(diào)用 127.0.0.1:20881 服務(wù)。

Tips: 小伙伴通過在消費端動態(tài)切換標(biāo)簽TAG_ATAG_A來查看效果黄痪,服務(wù)端只需啟動一個端口為20880的服務(wù)即可。

<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:block; text-align:center;"
data-ad-layout="in-article"
data-ad-format="fluid"
data-ad-client="ca-pub-4279907681900931"
data-ad-slot="6812672741"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

5. 實現(xiàn)原理

根據(jù)前面的介紹我們知道在消費端調(diào)用遠(yuǎn)程服務(wù)時通過路由規(guī)則進(jìn)行服務(wù)的過濾盔然,那么我們通過源碼簡單的分析下這個處理過程桅打。這里我們直接看到路由規(guī)則的調(diào)用核心代碼org.apache.dubbo.rpc.cluster. RouterChain#route核心方法如下:

    public List<Invoker<T>> route(URL url, Invocation invocation) {
        List<Invoker<T>> finalInvokers = invokers;
        for (Router router : routers) {
            finalInvokers = router.route(finalInvokers, url, invocation);
        }
        return finalInvokers;
    }

下面展示了我們運行過程中的路由規(guī)則:

idea3

其中TagRouter就是我們的標(biāo)簽路由核心代碼如下:

/**
     *
     * 標(biāo)簽路由
     *
     * @author liyong
     * @date 4:48 PM 2020/11/29
     * @param invokers
     * @param url
     * @param invocation
     * @exception
     * @return java.util.List<org.apache.dubbo.rpc.Invoker<T>>
     **/
    @Override
    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        if (CollectionUtils.isEmpty(invokers)) {
            return invokers;
        }

        // 這里因為配置中心可能更新配置,所有使用另外一個常量引用(類似復(fù)制)
        final TagRouterRule tagRouterRuleCopy = tagRouterRule;
        //如果動態(tài)規(guī)則不存在或無效或沒有激活愈案,使用靜態(tài)標(biāo)簽
        if (tagRouterRuleCopy == null || !tagRouterRuleCopy.isValid() || !tagRouterRuleCopy.isEnabled()) {
            //處理靜態(tài)標(biāo)簽
            return filterUsingStaticTag(invokers, url, invocation);
        }

        List<Invoker<T>> result = invokers;
        //獲取上下文中Attachment的標(biāo)簽參數(shù)挺尾,這個參數(shù)由客戶端調(diào)用時候?qū)懭?        String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) :
                invocation.getAttachment(TAG_KEY);

        // 如果存在傳遞標(biāo)簽
        if (StringUtils.isNotEmpty(tag)) {
            //通過傳遞的標(biāo)簽找到動態(tài)配置對應(yīng)的服務(wù)地址
            List<String> addresses = tagRouterRuleCopy.getTagnameToAddresses().get(tag);
            // 通過標(biāo)簽分組進(jìn)行過濾
            if (CollectionUtils.isNotEmpty(addresses)) {
                //獲取匹配地址的服務(wù)
                result = filterInvoker(invokers, invoker -> addressMatches(invoker.getUrl(), addresses));
                //如果返回結(jié)果不為null 或者 返回結(jié)果為空但是配置force=true也直接返回
                if (CollectionUtils.isNotEmpty(result) || tagRouterRuleCopy.isForce()) {
                    return result;
                }
            } else {
                //檢測靜態(tài)標(biāo)簽
                result = filterInvoker(invokers, invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY)));
            }
            //如果提供者沒有配置標(biāo)簽 默認(rèn)force.tag = false 表示可以訪問任意的提供者 ,除非我們顯示的禁止
            if (CollectionUtils.isNotEmpty(result) || isForceUseTag(invocation)) {
                return result;
            }
            else {
                //返回所有的提供者站绪,不需要任意標(biāo)簽
                List<Invoker<T>> tmp = filterInvoker(invokers, invoker -> addressNotMatches(invoker.getUrl(),
                        tagRouterRuleCopy.getAddresses()));
                //查找提供者標(biāo)簽為空
                return filterInvoker(tmp, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY)));
            }
        } else {
            //返回所有的 addresses
            List<String> addresses = tagRouterRuleCopy.getAddresses();
            if (CollectionUtils.isNotEmpty(addresses)) {
                result = filterInvoker(invokers, invoker -> addressNotMatches(invoker.getUrl(), addresses));
                // 1. all addresses are in dynamic tag group, return empty list.
                if (CollectionUtils.isEmpty(result)) {
                    return result;
                }
            }
           //繼續(xù)使用靜態(tài)標(biāo)簽過濾
            return filterInvoker(result, invoker -> {
                String localTag = invoker.getUrl().getParameter(TAG_KEY);
                return StringUtils.isEmpty(localTag) || !tagRouterRuleCopy.getTagNames().contains(localTag);
            });
        }
    }

上面的代碼中把主要的流程進(jìn)行注釋遭铺,請小伙伴自行進(jìn)行代碼調(diào)試查看。

6. 小結(jié)

在本小節(jié)中我們主要學(xué)習(xí)了 Dubbo 中路由規(guī)則之標(biāo)簽路由以及使用方式恢准。同時也分析了標(biāo)簽路由規(guī)則實現(xiàn)的原理:如果消費端傳遞標(biāo)簽則和配置的動態(tài)規(guī)則和靜態(tài)規(guī)則進(jìn)行匹配魂挂,如果消費端未傳遞標(biāo)簽則使用服務(wù)提供端的本地配置的靜態(tài)標(biāo)簽和動態(tài)配置標(biāo)簽進(jìn)行匹配。

Tips: 動態(tài)規(guī)則相較于靜態(tài)規(guī)則優(yōu)先級更高馁筐,而當(dāng)兩種規(guī)則同時存在且出現(xiàn)沖突時涂召,將以動態(tài)規(guī)則為準(zhǔn)。

本節(jié)課程的重點如下:

  1. 理解 Dubbo 標(biāo)簽路由

  2. 了解了標(biāo)簽路由使用方式

  3. 了解標(biāo)簽路由實現(xiàn)原理

  4. 了解標(biāo)簽路由使用場景

作者

個人從事金融行業(yè)敏沉,就職過易極付果正、思建科技、某網(wǎng)約車平臺等重慶一流技術(shù)團(tuán)隊盟迟,目前就職于某銀行負(fù)責(zé)統(tǒng)一支付系統(tǒng)建設(shè)秋泳。自身對金融行業(yè)有強(qiáng)烈的愛好。同時也實踐大數(shù)據(jù)攒菠、數(shù)據(jù)存儲迫皱、自動化集成和部署、分布式微服務(wù)要尔、響應(yīng)式編程舍杜、人工智能等領(lǐng)域新娜。同時也熱衷于技術(shù)分享創(chuàng)立公眾號和博客站點對知識體系進(jìn)行分享。關(guān)注公眾號:青年IT男 獲取最新技術(shù)文章推送既绩!

博客地址: http://youngitman.tech

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末概龄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子饲握,更是在濱河造成了極大的恐慌私杜,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件救欧,死亡現(xiàn)場離奇詭異衰粹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)笆怠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門铝耻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹬刷,你說我怎么就攤上這事瓢捉。” “怎么了办成?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵泡态,是天一觀的道長。 經(jīng)常有香客問我迂卢,道長某弦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任而克,我火速辦了婚禮靶壮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拍摇。我一直安慰自己亮钦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布充活。 她就那樣靜靜地躺著蜂莉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪混卵。 梳的紋絲不亂的頭發(fā)上映穗,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機(jī)與錄音幕随,去河邊找鬼蚁滋。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辕录。 我是一名探鬼主播睦霎,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼走诞!你這毒婦竟也來了副女?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤蚣旱,失蹤者是張志新(化名)和其女友劉穎碑幅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塞绿,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡沟涨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了异吻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裹赴。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涧黄,靈堂內(nèi)的尸體忽然破棺而出篮昧,到底是詐尸還是另有隱情,我是刑警寧澤笋妥,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站窄潭,受9級特大地震影響春宣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫉你,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一月帝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幽污,春花似錦嚷辅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至准潭,卻和暖如春趁俊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刑然。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工寺擂, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓怔软,卻偏偏與公主長得像垦细,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挡逼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容