前言
大家好,今天開始給大家分享 — 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)簽路由:
上圖中我們可以看到有兩個機(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=tag1
的provider
代态。若集群中不存在與請求標(biāo)記對應(yīng)的服務(wù)寺惫,默認(rèn)將降級請求 tag為空的provider
;如果要改變這種默認(rèn)行為蹦疑,即找不到匹配tag1
的provider
返回異常西雀,需設(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ù)提供者配置兩個端口:20880
、20881
甚疟,然后分別指定兩個服務(wù)標(biāo)簽為:TAG_A
仗岖、TAG_B
。項目結(jié)構(gòu)圖如下:
這里我們使用動態(tài)打標(biāo)的方式所有 XML 中的配置維持以前案例的配置古拴,我們主要看看 Dubbo Admin 中的配置:
# 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_A
和TAG_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ī)則:
其中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é)課程的重點如下:
理解 Dubbo 標(biāo)簽路由
了解了標(biāo)簽路由使用方式
了解標(biāo)簽路由實現(xiàn)原理
了解標(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