前言
在Dubbo——路由機(jī)制(上),介紹了 Router 接口的基本功能以及 RouterChain 加載多個(gè) Router 的實(shí)現(xiàn)宛蚓,之后介紹了 ConditionRouter 這個(gè)類(lèi)對(duì)條件路由規(guī)則的處理邏輯以及 ScriptRouter 這個(gè)類(lèi)對(duì)腳本路由規(guī)則的處理邏輯洲押。本文繼續(xù)介紹剩余的三個(gè) Router 接口實(shí)現(xiàn)類(lèi)。
FileRouterFactory
FileRouterFactory 是 ScriptRouterFactory 的裝飾器沧竟,其擴(kuò)展名為 file铸敏,F(xiàn)ileRouterFactory 在 ScriptRouterFactory 基礎(chǔ)上增加了讀取文件的能力∥虮茫可以將 ScriptRouter 使用的路由規(guī)則保存到文件中杈笔,然后在 URL 中指定文件路徑,F(xiàn)ileRouterFactory 從中解析到該腳本文件的路徑并進(jìn)行讀取糕非,調(diào)用 ScriptRouterFactory 去創(chuàng)建相應(yīng)的 ScriptRouter 對(duì)象蒙具。
下面來(lái)看 FileRouterFactory 對(duì) getRouter() 方法的具體實(shí)現(xiàn),其中完成了 file 協(xié)議的 URL 到 script 協(xié)議 URL 的轉(zhuǎn)換朽肥,如下是一個(gè)轉(zhuǎn)換示例禁筏,首先會(huì)將 file:// 協(xié)議轉(zhuǎn)換成 script:// 協(xié)議,然后會(huì)添加 type 參數(shù)和 rule 參數(shù)衡招,其中 type 參數(shù)值根據(jù)文件后綴名確定篱昔,該示例為 js,rule 參數(shù)值為文件內(nèi)容蚁吝。
可以再結(jié)合接下來(lái)這個(gè)示例分析 getRouter() 方法的具體實(shí)現(xiàn):
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=<file-content>
// 默認(rèn)使用script協(xié)議
String protocol = url.getParameter(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 ...
String path = url.getPath();
// 獲取腳本文件的語(yǔ)言類(lèi)型
if (path != null) {
int i = path.lastIndexOf('.');
if (i > 0) {
type = path.substring(i + 1);
}
}
// 讀取腳本文件中的內(nèi)容
String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
// FIXME: this code looks useless
boolean runtime = url.getParameter(RUNTIME_KEY, false);
// 創(chuàng)建script協(xié)議的URL
URL script = URLBuilder.from(url)
.setProtocol(protocol)
.addParameter(TYPE_KEY, type)
.addParameter(RUNTIME_KEY, runtime)
.addParameterAndEncoded(RULE_KEY, rule)
.build();
// 獲取script對(duì)應(yīng)的Router實(shí)現(xiàn)
return routerFactory.getRouter(script);
} catch (IOException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
TagRouterFactory & TagRouter
TagRouterFactory 作為 RouterFactory 接口的擴(kuò)展實(shí)現(xiàn)旱爆,其擴(kuò)展名為 tag。但是需要注意的是窘茁,TagRouterFactory 與之前介紹的 ConditionRouterFactory怀伦、ScriptRouterFactory 的不同之處在于,它是通過(guò)繼承 CacheableRouterFactory 這個(gè)抽象類(lèi)山林,間接實(shí)現(xiàn)了 RouterFactory 接口房待。
CacheableRouterFactory 抽象類(lèi)中維護(hù)了一個(gè) ConcurrentMap 集合(routerMap 字段)用來(lái)緩存 Router,其中的 Key 是 ServiceKey驼抹。在 CacheableRouterFactory 的 getRouter() 方法中桑孩,會(huì)優(yōu)先根據(jù) URL 的 ServiceKey 查詢(xún) routerMap 集合,查詢(xún)失敗之后會(huì)調(diào)用 createRouter() 抽象方法來(lái)創(chuàng)建相應(yīng)的 Router 對(duì)象框冀。在 TagRouterFactory.createRouter() 方法中流椒,創(chuàng)建的自然就是 TagRouter 對(duì)象了。
基于 Tag 的測(cè)試環(huán)境隔離方案
通過(guò) TagRouter明也,可以將某一個(gè)或多個(gè) Provider 劃分到同一分組宣虾,約束流量只在指定分組中流轉(zhuǎn)惯裕,這樣就可以輕松達(dá)到流量隔離的目的,從而支持灰度發(fā)布等場(chǎng)景绣硝。
目前蜻势,Dubbo 提供了動(dòng)態(tài)和靜態(tài)兩種方式給 Provider 打標(biāo)簽,其中動(dòng)態(tài)方式就是通過(guò)服務(wù)治理平臺(tái)動(dòng)態(tài)下發(fā)標(biāo)簽鹉胖,靜態(tài)方式就是在 XML 等靜態(tài)配置中打標(biāo)簽握玛。Consumer 端可以在 RpcContext 的 attachment 中添加 request.tag 附加屬性,注意保存在 attachment 中的值將會(huì)在一次完整的遠(yuǎn)程調(diào)用中持續(xù)傳遞甫菠,我們只需要在起始調(diào)用時(shí)進(jìn)行設(shè)置挠铲,就可以達(dá)到標(biāo)簽的持續(xù)傳遞。
了解了 Tag 的基本概念和功能之后淑蔚,再簡(jiǎn)單介紹一個(gè) Tag 的使用示例市殷。
在實(shí)際的開(kāi)發(fā)測(cè)試中,一個(gè)完整的請(qǐng)求會(huì)涉及非常多的 Provider刹衫,分屬不同團(tuán)隊(duì)進(jìn)行維護(hù)醋寝,這些團(tuán)隊(duì)每天都會(huì)處理不同的需求,并在其負(fù)責(zé)的 Provider 服務(wù)中進(jìn)行修改带迟,如果所有團(tuán)隊(duì)都使用一套測(cè)試環(huán)境音羞,那么測(cè)試環(huán)境就會(huì)變得很不穩(wěn)定。如下圖所示仓犬,4 個(gè) Provider 分屬不同的團(tuán)隊(duì)管理嗅绰,Provider 2 和 Provider 4 在測(cè)試環(huán)境測(cè)試,部署了有 Bug 的版本搀继,這樣就會(huì)導(dǎo)致整個(gè)測(cè)試環(huán)境無(wú)法正常處理請(qǐng)求窘面,在這樣一個(gè)不穩(wěn)定的測(cè)試環(huán)境中排查 Bug 是非常困難的,因?yàn)榭赡芘挪榈阶詈筮辞l(fā)現(xiàn)是別人的 Bug财边。
為了解決上述問(wèn)題,我們可以針對(duì)每個(gè)需求分別獨(dú)立出一套測(cè)試環(huán)境点骑,但是這個(gè)方案會(huì)占用大量機(jī)器酣难,前期的搭建成本以及后續(xù)的維護(hù)成本也都非常高。
下面是一個(gè)通過(guò) Tag 方式實(shí)現(xiàn)環(huán)境隔離的架構(gòu)圖黑滴,其中憨募,需求 1 對(duì) Provider 2 的請(qǐng)求會(huì)全部落到有需求 1 標(biāo)簽的 Provider 上,其他 Provider 使用穩(wěn)定測(cè)試環(huán)境中的 Provider袁辈;需求 2 對(duì) Provider 4 的請(qǐng)求會(huì)全部落到有需求 2 標(biāo)簽的 Provider 4 上菜谣,其他 Provider 使用穩(wěn)定測(cè)試環(huán)境中的 Provider。
在一些特殊場(chǎng)景中,會(huì)有 Tag 降級(jí)的場(chǎng)景葛菇,比如找不到對(duì)應(yīng) Tag 的 Provider甘磨,會(huì)按照一定的規(guī)則進(jìn)行降級(jí)。如果在 Provider 集群中不存在與請(qǐng)求 Tag 對(duì)應(yīng)的 Provider 節(jié)點(diǎn)眯停,則默認(rèn)將降級(jí)請(qǐng)求 Tag 為空的 Provider;如果希望在找不到匹配 Tag 的 Provider 節(jié)點(diǎn)時(shí)拋出異常的話(huà)卿泽,我們需設(shè)置 request.tag.force = true莺债。
如果請(qǐng)求中的 request.tag 未設(shè)置,只會(huì)匹配 Tag 為空的 Provider签夭,也就是說(shuō)即使集群中存在可用的服務(wù)齐邦,若 Tag 不匹配也就無(wú)法調(diào)用。一句話(huà)總結(jié)第租,攜帶 Tag 的請(qǐng)求可以降級(jí)訪(fǎng)問(wèn)到無(wú) Tag 的 Provider措拇,但不攜帶 Tag 的請(qǐng)求永遠(yuǎn)無(wú)法訪(fǎng)問(wèn)到帶有 Tag 的 Provider。
TagRouter
下面再來(lái)看 TagRouter 的具體實(shí)現(xiàn)慎宾。在 TagRouter 中持有一個(gè) TagRouterRule 對(duì)象的引用丐吓,在 TagRouterRule 中維護(hù)了一個(gè) Tag 集合,而在每個(gè) Tag 對(duì)象中又都維護(hù)了一個(gè) Tag 的名稱(chēng)趟据,以及 Tag 綁定的網(wǎng)絡(luò)地址集合券犁,如下圖所示:
另外粘衬,在 TagRouterRule 中還維護(hù)了 addressToTagnames、tagnameToAddresses 兩個(gè)集合(都是 Map<String, List<String>> 類(lèi)型)咳促,分別記錄了 Tag 名稱(chēng)到各個(gè) address 的映射以及 address 到 Tag 名稱(chēng)的映射泵肄。在 TagRouterRule 的 init() 方法中备籽,會(huì)根據(jù) tags 集合初始化這兩個(gè)集合。
了解了 TagRouterRule 的基本構(gòu)造之后,我們繼續(xù)來(lái)看 TagRouter 構(gòu)造 TagRouterRule 的過(guò)程泞莉。TagRouter 除了實(shí)現(xiàn)了 Router 接口之外,還實(shí)現(xiàn)了 ConfigurationListener 接口具温,如下圖所示:
ConfigurationListener 用于監(jiān)聽(tīng)配置的變化硅则,其中就包括 TagRouterRule 配置的變更。當(dāng)我們通過(guò)動(dòng)態(tài)更新 TagRouterRule 配置的時(shí)候噪裕,就會(huì)觸發(fā) ConfigurationListener 接口的 process() 方法蹲盘,TagRouter 對(duì) process() 方法的實(shí)現(xiàn)如下:
public class TagRouter extends AbstractRouter implements ConfigurationListener {
@Override
public synchronized void process(ConfigChangedEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("Notification of tag rule, change type is: " + event.getChangeType() + ", raw rule is:\n " +
event.getContent());
}
try {
if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
// DELETED事件會(huì)直接清空tagRouterRule
this.tagRouterRule = null;
} else {
// 其他事件會(huì)解析最新的路由規(guī)則,并記錄到tagRouterRule字段中
this.tagRouterRule = TagRuleParser.parse(event.getContent());
}
} catch (Exception e) {
logger.error("Failed to parse the raw tag router rule and it will not take effect, please check if the " +
"rule matches with the template, the raw rule is:\n ", e);
}
}
}
我們可以看到膳音,如果是刪除配置的操作召衔,則直接將 tagRouterRule 設(shè)置為 null,如果是修改或新增配置祭陷,則通過(guò) TagRuleParser 解析傳入的配置苍凛,得到對(duì)應(yīng)的 TagRouterRule 對(duì)象趣席。TagRuleParser 可以解析 yaml 格式的 TagRouterRule 配置,下面是一個(gè)配置示例:
force: false
runtime: true
enabled: false
priority: 1
key: demo-provider
tags:
- name: tag1
addresses: null
- name: tag2
addresses: ["30.5.120.37:20880"]
- name: tag3
addresses: []
經(jīng)過(guò) TagRuleParser 解析得到的 TagRouterRule 結(jié)構(gòu)醇蝴,如下所示:
除了上圖展示的幾個(gè)集合字段宣肚,TagRouterRule 還從 AbstractRouterRule 抽象類(lèi)繼承了一些控制字段,后面介紹的 ConditionRouterRule 也繼承了 AbstractRouterRule悠栓。
AbstractRouterRule 中核心字段的具體含義大致可總結(jié)為如下:
key(string 類(lèi)型)霉涨、scope(string 類(lèi)型):key 明確規(guī)則體作用在哪個(gè)服務(wù)或應(yīng)用。scope 為 service 時(shí)惭适,key 由 [{group}:]{service}[:{version}] 構(gòu)成笙瑟;scope 為 application 時(shí),key 為 application 的名稱(chēng)癞志。
rawRule(string 類(lèi)型):記錄了路由規(guī)則解析前的原始字符串配置往枷。
runtime(boolean 類(lèi)型):表示是否在每次調(diào)用時(shí)執(zhí)行該路由規(guī)則。如果設(shè)置為 false凄杯,則會(huì)在 Provider 列表變更時(shí)預(yù)先執(zhí)行并緩存結(jié)果错洁,調(diào)用時(shí)直接從緩存中獲取路由結(jié)果。
force(boolean 類(lèi)型):當(dāng)路由結(jié)果為空時(shí)盾舌,是否強(qiáng)制執(zhí)行墓臭,如果不強(qiáng)制執(zhí)行,路由結(jié)果為空的路由規(guī)則將自動(dòng)失效妖谴。該字段默認(rèn)值為 false窿锉。
valid(boolean 類(lèi)型):用于標(biāo)識(shí)解析生成當(dāng)前 RouterRule 對(duì)象的配置是否合法。
enabled(boolean 類(lèi)型):標(biāo)識(shí)當(dāng)前路由規(guī)則是否生效膝舅。
priority(int 類(lèi)型):用于表示當(dāng)前 RouterRule 的優(yōu)先級(jí)嗡载。
dynamic(boolean 類(lèi)型):表示該路由規(guī)則是否為持久數(shù)據(jù),當(dāng)注冊(cè)方退出時(shí)仍稀,路由規(guī)則是否依然存在洼滚。
我們可以看到,AbstractRouterRule 中的核心字段與前面的示例配置是一一對(duì)應(yīng)的技潘。
我們知道遥巴,Router 最終目的是要過(guò)濾符合條件的 Invoker 對(duì)象,下面我們一起來(lái)看 TagRouter 是如何使用 TagRouterRule 路由邏輯進(jìn)行 Invoker 過(guò)濾的享幽,大致步驟如下:
1铲掐、如果 invokers 為空,直接返回空集合值桩。
2摆霉、檢查關(guān)聯(lián)的 tagRouterRule 對(duì)象是否可用,如果不可用,則會(huì)直接調(diào)用 filterUsingStaticTag() 方法進(jìn)行過(guò)濾携栋,并返回過(guò)濾結(jié)果搭盾。在 filterUsingStaticTag() 方法中,會(huì)比較請(qǐng)求攜帶的 tag 值與 Provider URL 中的 tag 參數(shù)值婉支。
3鸯隅、獲取此次調(diào)用的 tag 信息,這里會(huì)嘗試從 Invocation 以及 URL 的參數(shù)中獲取向挖。
-
4滋迈、如果此次請(qǐng)求指定了 tag 信息,則首先會(huì)獲取 tag 關(guān)聯(lián)的 address 集合户誓。
如果 address 集合不為空,則根據(jù)該 address 集合中的地址幕侠,匹配出符合條件的 Invoker 集合帝美。如果存在符合條件的 Invoker,則直接將過(guò)濾得到的 Invoker 集合返回晤硕;如果不存在悼潭,就會(huì)根據(jù) force 配置決定是否返回空 Invoker 集合。
如果 address 集合為空舞箍,則會(huì)將請(qǐng)求攜帶的 tag 值與 Provider URL 中的 tag 參數(shù)值進(jìn)行比較舰褪,匹配出符合條件的 Invoker 集合。如果存在符合條件的 Invoker疏橄,則直接將過(guò)濾得到的 Invoker 集合返回占拍;如果不存在捎迫,就會(huì)根據(jù) force 配置決定是否返回空 Invoker 集合。
如果 force 配置為 false贝次,且符合條件的 Invoker 集合為空,則返回所有不包含任何 tag 的 Provider 列表彰导。
5、如果此次請(qǐng)求未攜帶 tag 信息位谋,則會(huì)先獲取 TagRouterRule 規(guī)則中全部 tag 關(guān)聯(lián)的 address 集合。如果 address 集合不為空倔幼,則過(guò)濾出不在 address 集合中的 Invoker 并添加到結(jié)果集合中盖腿,最后,將 Provider URL 中的 tag 值與 TagRouterRule 中的 tag 名稱(chēng)進(jìn)行比較翩腐,得到最終的 Invoker 集合茂卦。
上述流程的具體實(shí)現(xiàn)是在 TagRouter.route() 方法中,如下所示:
public class TagRouter extends AbstractRouter implements ConfigurationListener {
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
if (CollectionUtils.isEmpty(invokers)) {
// 如果invokers為空处渣,直接返回空集合
return invokers;
}
// since the rule can be changed by config center, we should copy one to use.
final TagRouterRule tagRouterRuleCopy = tagRouterRule;
if (tagRouterRuleCopy == null || !tagRouterRuleCopy.isValid() || !tagRouterRuleCopy.isEnabled()) {
return filterUsingStaticTag(invokers, url, invocation);
}
// 檢查關(guān)聯(lián)的tagRouterRule對(duì)象是否可用蛛砰,如果不可用泥畅,則會(huì)直接調(diào)用filterUsingStaticTag() 方法進(jìn)行過(guò)濾
List<Invoker<T>> result = invokers;
// 獲取此次調(diào)用的tag信息,嘗試從Invocation以及URL中獲取
String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) :
invocation.getAttachment(TAG_KEY);
// if we are requesting for a Provider with a specific tag
if (StringUtils.isNotEmpty(tag)) {// 此次請(qǐng)求一個(gè)特殊的tag
// 獲取tag關(guān)聯(lián)的address集合
List<String> addresses = tagRouterRuleCopy.getTagnameToAddresses().get(tag);
// filter by dynamic tag group first
if (CollectionUtils.isNotEmpty(addresses)) {
// 根據(jù)上面的address集合匹配符合條件的Invoker
result = filterInvoker(invokers, invoker -> addressMatches(invoker.getUrl(), addresses));
// if result is not null OR it's null but force=true, return result directly
// 如果存在符合條件的Invoker柑贞,則直接將過(guò)濾得到的Invoker集合返回
// 如果不存在符合條件的Invoker钧嘶,根據(jù)force配置決定是否返回空Invoker集合
if (CollectionUtils.isNotEmpty(result) || tagRouterRuleCopy.isForce()) {
return result;
}
} else {
// dynamic tag group doesn't have any item about the requested app OR it's null after filtered by
// dynamic tag group but force=false. check static tag
// 如果 address 集合為空琳疏,則會(huì)將請(qǐng)求攜帶的 tag 與 Provider URL 中的 tag 參數(shù)值進(jìn)行比較轿亮,匹配出符合條件的 Invoker 集合。
result = filterInvoker(invokers, invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY)));
}
// If there's no tagged providers that can match the current tagged request. force.tag is set by default
// to false, which means it will invoke any providers without a tag unless it's explicitly disallowed.
if (CollectionUtils.isNotEmpty(result) || isForceUseTag(invocation)) {
// 存在符合條件的Invoker或是force配置為true
return result;
}
// FAILOVER: return all Providers without any tags.
else {
// 如果 force 配置為 false按咒,且符合條件的 Invoker 集合為空但骨,則返回所有不包含任何 tag 的 Provider 列表奔缠。
List<Invoker<T>> tmp = filterInvoker(invokers, invoker -> addressNotMatches(invoker.getUrl(),
tagRouterRuleCopy.getAddresses()));
return filterInvoker(tmp, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY)));
}
} else {
// List<String> addresses = tagRouterRule.filter(providerApp);
// return all addresses in dynamic tag group.
// 如果此次請(qǐng)求未攜帶 tag 信息,則會(huì)先獲取 TagRouterRule 規(guī)則中全部 tag 關(guān)聯(lián)的 address 集合两波。
List<String> addresses = tagRouterRuleCopy.getAddresses();
if (CollectionUtils.isNotEmpty(addresses)) {
// 如果 address 集合不為空,則過(guò)濾出不在 address 集合中的 Invoker 并添加到結(jié)果集合中单起。
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;
}
// 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the
// static tag group.
}
// 如果不存在符合條件的 Invoker 或是 address 集合為空嘀倒,則會(huì)將請(qǐng)求攜帶的 tag 與 Provider URL 中的 tag 參數(shù)值進(jìn)行比較局冰,得到最終的 Invoker 集合康二。
return filterInvoker(result, invoker -> {
String localTag = invoker.getUrl().getParameter(TAG_KEY);
return StringUtils.isEmpty(localTag) || !tagRouterRuleCopy.getTagNames().contains(localTag);
});
}
}
}
ServiceRouter & AppRouter
除了之前介紹的 TagRouterFactory 繼承了 CacheableRouterFactory 之外,ServiceRouterFactory 也繼承 CachabelRouterFactory固逗,具有了緩存的能力藕帜,具體繼承關(guān)系如下圖所示:
ServiceRouterFactory 創(chuàng)建的 Router 實(shí)現(xiàn)是 ServiceRouter洽故,與 ServiceRouter 類(lèi)似的是 AppRouter时甚,兩者都繼承了 ListenableRouter 抽象類(lèi)(雖然 ListenableRouter 是個(gè)抽象類(lèi)哈踱,但是沒(méi)有抽象方法留給子類(lèi)實(shí)現(xiàn))开镣,繼承關(guān)系如下圖所示:
ListenableRouter 在 ConditionRouter 基礎(chǔ)上添加了動(dòng)態(tài)配置的能力,ListenableRouter 的 process() 方法與 TagRouter 中的 process() 方法類(lèi)似陕壹,對(duì)于 ConfigChangedEvent.DELETE 事件树埠,直接清空 ListenableRouter 中維護(hù)的 ConditionRouterRule 和 ConditionRouter 集合的引用怎憋;對(duì)于 ADDED九昧、UPDATED 事件铸鹰,則通過(guò) ConditionRuleParser 解析事件內(nèi)容期揪,得到相應(yīng)的 ConditionRouterRule 對(duì)象和 ConditionRouter 集合。這里的 ConditionRuleParser 同樣是以 yaml 文件的格式解析 ConditionRouterRule 的相關(guān)配置姓建。ConditionRouterRule 中維護(hù)了一個(gè) conditions 集合(List<String> 類(lèi)型)速兔,記錄了多個(gè) Condition 路由規(guī)則活玲,對(duì)應(yīng)生成多個(gè) ConditionRouter 對(duì)象。
整個(gè)解析 ConditionRouterRule 的過(guò)程镀钓,與前文介紹的解析 TagRouterRule 的流程類(lèi)似丁溅。
在 ListenableRouter 的 route() 方法中探遵,會(huì)遍歷全部 ConditionRouter 過(guò)濾出符合全部路由條件的 Invoker 集合,具體實(shí)現(xiàn)如下:
public abstract class ListenableRouter extends AbstractRouter implements ConfigurationListener {
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
if (CollectionUtils.isEmpty(invokers) || conditionRouters.size() == 0) {
// 檢查邊界條件涯穷,直接返回invokers集合
return invokers;
}
// We will check enabled status inside each router.
// 路由規(guī)則進(jìn)行過(guò)濾
for (Router router : conditionRouters) {
invokers = router.route(invokers, url, invocation);
}
return invokers;
}
}
ServiceRouter 和 AppRouter 都是簡(jiǎn)單地繼承了 ListenableRouter 抽象類(lèi)拷况,且沒(méi)有覆蓋 ListenableRouter 的任何方法掘殴,兩者只有以下兩點(diǎn)區(qū)別杯巨。
一個(gè)是 priority 字段值不同。ServiceRouter 為 140杜恰,AppRouter 為 150,也就是說(shuō) ServiceRouter 要先于 AppRouter 執(zhí)行舔涎。
另一個(gè)是獲取 ConditionRouterRule 配置的 Key 不同亡嫌。ServiceRouter 使用的 RuleKey 是由 {interface}:[version]:[group] 三部分構(gòu)成掘而,獲取的是一個(gè)服務(wù)對(duì)應(yīng)的 ConditionRouterRule。AppRouter 使用的 RuleKey 是 URL 中的 application 參數(shù)值知染,獲取的是一個(gè)服務(wù)實(shí)例對(duì)應(yīng)的 ConditionRouterRule控淡。
總結(jié)
本文我們是緊接Dubbo——路由機(jī)制(上)的內(nèi)容止潘,繼續(xù)介紹了剩余 Router 接口實(shí)現(xiàn)的內(nèi)容凭戴。
我們介紹了基于文件的 FileRouter 實(shí)現(xiàn),其底層會(huì)依賴(lài)之前介紹的 ScriptRouter;接下來(lái)又講解了基于 Tag 的測(cè)試環(huán)境隔離方案魏割,以及如何基于 TagRouter 實(shí)現(xiàn)該方案钢颂,同時(shí)深入分析了 TagRouter 的核心實(shí)現(xiàn);最后我們還介紹了 ListenableRouter 抽象類(lèi)以及 ServerRouter 和 AppRouter 兩個(gè)實(shí)現(xiàn)遭垛,它們是在條件路由的基礎(chǔ)上添加了動(dòng)態(tài)變更路由規(guī)則的能力锯仪,同時(shí)區(qū)分了服務(wù)級(jí)別和服務(wù)實(shí)例級(jí)別的配置趾盐。