route 方法先是调用 matchWhen 对服务消费者进行匹配,如果匹配失败,直接返回 Invoker 列表。如果匹配成功,再对服务提供者进行匹配,匹配逻辑封装在了 matchThen 方法中。下面来看一下这两个方法的逻辑:
// 服务消费者条件为 null 或空,均返回 true,比如:
// => host != 172.22.3.91
// 表示所有的服务消费者都不得调用 IP 为 172.22.3.91 的机器上的服务
return whenCondition == null || whenCondition.isEmpty()
|| matchCondition(whenCondition, url, null, invocation); // 进行条件匹配
}
private boolean matchThen(URL url, URL param) {
// 服务提供者条件为 null 或空,表示禁用服务
return !(thenCondition == null || thenCondition.isEmpty())
&& matchCondition(thenCondition, url, param, null); // 进行条件匹配
}
这两个方法长的有点像,不过逻辑上还是有差别的,大家注意看。这两个方法均调用了 matchCondition 方法,但它们所传入的参数是不同的。这个需要特别注意一下,不然后面的逻辑不好弄懂。下面我们对这几个参数进行溯源。matchWhen 方法向 matchCondition 方法传入的参数为 [whenCondition, url, null, invocation],第一个参数 whenCondition 为服务消费者匹配条件,这个前面分析过。第二个参数 url 源自 route 方法的参数列表,该参数由外部类调用 route 方法时传入。比如:
接下来再来看看 matchThen 向 matchCondition 方法传入的参数 [thenCondition, url, param, null]。第一个参数不用解释了。第二个和第三个参数来自 matchThen 方法的参数列表,这两个参数分别为服务提供者 url 和服务消费者 url。搞清楚这些参数来源后,接下来就可以分析 matchCondition 方法了。
private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param, Invocation invocation) {
// 将服务提供者或消费者 url 转成 Map
Map<String, String> sample = url.toMap();
boolean result = false;
// 遍历 condition 列表
for (Map.Entry<String, MatchPair> matchPair : condition.entrySet()) {
// 获取匹配项名称,比如 host、method 等
String key = matchPair.getKey();
String sampleValue;
// 如果 invocation 不为空,且 key 为 mehtod(s),表示进行方法匹配
if (invocation != null && (Constants.METHOD_KEY.equals(key) || Constants.METHODS_KEY.equals(key))) {
// 从 invocation 获取被调用方法的名称
sampleValue = invocation.getMethodName();
} else {
// 从服务提供者或消费者 url 中获取指定字段值,比如 host、application 等
sampleValue = sample.get(key);
if (sampleValue == null) {
// 尝试通过 default.xxx 获取相应的值
sampleValue = sample.get(Constants.DEFAULT_KEY_PREFIX + key);
}
}
if (sampleValue != null) {
// 调用 MatchPair 的 isMatch 方法进行匹配
if (!matchPair.getValue().isMatch(sampleValue, param)) {
// 只要有一个规则匹配失败,立即返回 false 结束方法逻辑
return false;
} else {
result = true;
}
} else {
// sampleValue 为空,表明服务提供者或消费者 url 中不包含相关字段。此时如果
// MatchPair 的 matches 不为空,表示匹配失败,返回 false。比如我们有这样
// 一条匹配条件 loadbalance = random,假设 url 中并不包含 loadbalance 参数,
// 此时 sampleValue = null。既然路由规则里限制了 loadbalance 必须为 random,
// 但 sampleValue = null,明显不符合规则,因此返回 false
if (!matchPair.getValue().matches.isEmpty()) {
return false;
} else {
result = true;
}
}
}
return result;
}
如上,matchCondition 方法看起来有点复杂,这里简单说明一下。分割线以上的代码实际上用于获取 sampleValue 的值,分割线以下才是进行条件匹配。条件匹配调用的逻辑封装在 isMatch 中,代码如下:
isMatch 方法是通过 UrlUtils 的 isMatchGlobPattern 方法进行匹配,因此下面我们再来看看 isMatchGlobPattern 方法的逻辑。
public static boolean isMatchGlobPattern(String pattern, String value, URL param) {
if (param != null && pattern.startsWith("$")) {
// 引用服务消费者参数,param 参数为服务消费者 url
pattern = param.getRawParameter(pattern.substring(1));
}
// 调用重载方法继续比较
return isMatchGlobPattern(pattern, value);
}
public static boolean isMatchGlobPattern(String pattern, String value) {
// 对 * 通配符提供支持
return true;
if ((pattern == null || pattern.length() == 0)
&& (value == null || value.length() == 0))
// pattern 和 value 均为空,此时可认为两者相等,返回 true
return true;
if ((pattern == null || pattern.length() == 0)
|| (value == null || value.length() == 0))
// pattern 和 value 其中有一个为空,表明两者不相等,返回 false
return false;
// 定位 * 通配符位置
int i = pattern.lastIndexOf('*');
if (i == -1) {
// 匹配规则中不包含通配符,此时直接比较 value 和 pattern 是否相等即可,并返回比较结果
return value.equals(pattern);
}
// 通配符 "*" 在匹配规则尾部,比如 10.0.21.*
else if (i == pattern.length() - 1) {
// 检测 value 是否以“不含通配符的匹配规则”开头,并返回结果。比如:
// pattern = 10.0.21.*,value = 10.0.21.12,此时返回 true
return value.startsWith(pattern.substring(0, i));
}
// 通配符 "*" 在匹配规则头部
else if (i == 0) {
// 检测 value 是否以“不含通配符的匹配规则”结尾,并返回结果
return value.endsWith(pattern.substring(i + 1));
}
// 通配符 "*" 在匹配规则中间位置
else {
// 通过通配符将 pattern 分成两半,得到 prefix 和 suffix
String prefix = pattern.substring(0, i);
String suffix = pattern.substring(i + 1);
// 检测 value 是否以 prefix 开头,且以 suffix 结尾,并返回结果
return value.startsWith(prefix) && value.endsWith(suffix);
以上就是 isMatchGlobPattern 两个重载方法的全部逻辑,这两个方法分别对普通的匹配过程,以及”引用消费者参数“和通配符匹配等特性提供了支持。这两个方法的逻辑不是很复杂,且代码中也进行了比较详细的注释,因此就不多说了。