route 方法先是调用 matchWhen 对服务消费者进行匹配,如果匹配失败,直接返回 Invoker 列表。如果匹配成功,再对服务提供者进行匹配,匹配逻辑封装在了 matchThen 方法中。下面来看一下这两个方法的逻辑:

    1. // 服务消费者条件为 null 或空,均返回 true,比如:
    2. // => host != 172.22.3.91
    3. // 表示所有的服务消费者都不得调用 IP 为 172.22.3.91 的机器上的服务
    4. return whenCondition == null || whenCondition.isEmpty()
    5. || matchCondition(whenCondition, url, null, invocation); // 进行条件匹配
    6. }
    7. private boolean matchThen(URL url, URL param) {
    8. // 服务提供者条件为 null 或空,表示禁用服务
    9. return !(thenCondition == null || thenCondition.isEmpty())
    10. && matchCondition(thenCondition, url, param, null); // 进行条件匹配
    11. }

    这两个方法长的有点像,不过逻辑上还是有差别的,大家注意看。这两个方法均调用了 matchCondition 方法,但它们所传入的参数是不同的。这个需要特别注意一下,不然后面的逻辑不好弄懂。下面我们对这几个参数进行溯源。matchWhen 方法向 matchCondition 方法传入的参数为 [whenCondition, url, null, invocation],第一个参数 whenCondition 为服务消费者匹配条件,这个前面分析过。第二个参数 url 源自 route 方法的参数列表,该参数由外部类调用 route 方法时传入。比如:

    接下来再来看看 matchThen 向 matchCondition 方法传入的参数 [thenCondition, url, param, null]。第一个参数不用解释了。第二个和第三个参数来自 matchThen 方法的参数列表,这两个参数分别为服务提供者 url 和服务消费者 url。搞清楚这些参数来源后,接下来就可以分析 matchCondition 方法了。

    1. private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param, Invocation invocation) {
    2. // 将服务提供者或消费者 url 转成 Map
    3. Map<String, String> sample = url.toMap();
    4. boolean result = false;
    5. // 遍历 condition 列表
    6. for (Map.Entry<String, MatchPair> matchPair : condition.entrySet()) {
    7. // 获取匹配项名称,比如 host、method 等
    8. String key = matchPair.getKey();
    9. String sampleValue;
    10. // 如果 invocation 不为空,且 key 为 mehtod(s),表示进行方法匹配
    11. if (invocation != null && (Constants.METHOD_KEY.equals(key) || Constants.METHODS_KEY.equals(key))) {
    12. // 从 invocation 获取被调用方法的名称
    13. sampleValue = invocation.getMethodName();
    14. } else {
    15. // 从服务提供者或消费者 url 中获取指定字段值,比如 host、application 等
    16. sampleValue = sample.get(key);
    17. if (sampleValue == null) {
    18. // 尝试通过 default.xxx 获取相应的值
    19. sampleValue = sample.get(Constants.DEFAULT_KEY_PREFIX + key);
    20. }
    21. }
    22. if (sampleValue != null) {
    23. // 调用 MatchPair 的 isMatch 方法进行匹配
    24. if (!matchPair.getValue().isMatch(sampleValue, param)) {
    25. // 只要有一个规则匹配失败,立即返回 false 结束方法逻辑
    26. return false;
    27. } else {
    28. result = true;
    29. }
    30. } else {
    31. // sampleValue 为空,表明服务提供者或消费者 url 中不包含相关字段。此时如果
    32. // MatchPair 的 matches 不为空,表示匹配失败,返回 false。比如我们有这样
    33. // 一条匹配条件 loadbalance = random,假设 url 中并不包含 loadbalance 参数,
    34. // 此时 sampleValue = null。既然路由规则里限制了 loadbalance 必须为 random,
    35. // 但 sampleValue = null,明显不符合规则,因此返回 false
    36. if (!matchPair.getValue().matches.isEmpty()) {
    37. return false;
    38. } else {
    39. result = true;
    40. }
    41. }
    42. }
    43. return result;
    44. }

    如上,matchCondition 方法看起来有点复杂,这里简单说明一下。分割线以上的代码实际上用于获取 sampleValue 的值,分割线以下才是进行条件匹配。条件匹配调用的逻辑封装在 isMatch 中,代码如下:

    isMatch 方法是通过 UrlUtils 的 isMatchGlobPattern 方法进行匹配,因此下面我们再来看看 isMatchGlobPattern 方法的逻辑。

    1. public static boolean isMatchGlobPattern(String pattern, String value, URL param) {
    2. if (param != null && pattern.startsWith("$")) {
    3. // 引用服务消费者参数,param 参数为服务消费者 url
    4. pattern = param.getRawParameter(pattern.substring(1));
    5. }
    6. // 调用重载方法继续比较
    7. return isMatchGlobPattern(pattern, value);
    8. }
    9. public static boolean isMatchGlobPattern(String pattern, String value) {
    10. // 对 * 通配符提供支持
    11. return true;
    12. if ((pattern == null || pattern.length() == 0)
    13. && (value == null || value.length() == 0))
    14. // pattern 和 value 均为空,此时可认为两者相等,返回 true
    15. return true;
    16. if ((pattern == null || pattern.length() == 0)
    17. || (value == null || value.length() == 0))
    18. // pattern 和 value 其中有一个为空,表明两者不相等,返回 false
    19. return false;
    20. // 定位 * 通配符位置
    21. int i = pattern.lastIndexOf('*');
    22. if (i == -1) {
    23. // 匹配规则中不包含通配符,此时直接比较 value 和 pattern 是否相等即可,并返回比较结果
    24. return value.equals(pattern);
    25. }
    26. // 通配符 "*" 在匹配规则尾部,比如 10.0.21.*
    27. else if (i == pattern.length() - 1) {
    28. // 检测 value 是否以“不含通配符的匹配规则”开头,并返回结果。比如:
    29. // pattern = 10.0.21.*,value = 10.0.21.12,此时返回 true
    30. return value.startsWith(pattern.substring(0, i));
    31. }
    32. // 通配符 "*" 在匹配规则头部
    33. else if (i == 0) {
    34. // 检测 value 是否以“不含通配符的匹配规则”结尾,并返回结果
    35. return value.endsWith(pattern.substring(i + 1));
    36. }
    37. // 通配符 "*" 在匹配规则中间位置
    38. else {
    39. // 通过通配符将 pattern 分成两半,得到 prefix 和 suffix
    40. String prefix = pattern.substring(0, i);
    41. String suffix = pattern.substring(i + 1);
    42. // 检测 value 是否以 prefix 开头,且以 suffix 结尾,并返回结果
    43. return value.startsWith(prefix) && value.endsWith(suffix);

    以上就是 isMatchGlobPattern 两个重载方法的全部逻辑,这两个方法分别对普通的匹配过程,以及”引用消费者参数“和通配符匹配等特性提供了支持。这两个方法的逻辑不是很复杂,且代码中也进行了比较详细的注释,因此就不多说了。