2021SC@SDUSC
服务路由服务路由的入口方法是 ConditionRouter 的 route 方法,该方法定义在 Router 接口中。实现代码如下:
publicList > route(List > invokers, URL url, Invocation invocation) throws RpcException { if (invokers == null || invokers.isEmpty()) { return invokers; } try { // 先对服务消费者条件进行匹配,如果匹配失败,表明服务消费者 url 不符合匹配规则, // 无需进行后续匹配,直接返回 Invoker 列表即可。比如下面的规则: // host = 10.20.153.10 => host = 10.0.0.10 // 这条路由规则希望 IP 为 10.20.153.10 的服务消费者调用 IP 为 10.0.0.10 机器上的服务。 // 当消费者 ip 为 10.20.153.11 时,matchWhen 返回 false,表明当前这条路由规则不适用于 // 当前的服务消费者,此时无需再进行后续匹配,直接返回即可。 if (!matchWhen(url, invocation)) { return invokers; } List > result = new ArrayList >(); // 服务提供者匹配条件未配置,表明对指定的服务消费者禁用服务,也就是服务消费者在黑名单中 if (thenCondition == null) { logger.warn("The current consumer in the service blacklist..."); return result; } // 这里可以简单的把 Invoker 理解为服务提供者,现在使用服务提供者匹配规则对 // Invoker 列表进行匹配 for (Invoker invoker : invokers) { // 若匹配成功,表明当前 Invoker 符合服务提供者匹配规则。 // 此时将 Invoker 添加到 result 列表中 if (matchThen(invoker.getUrl(), url)) { result.add(invoker); } } // 返回匹配结果,如果 result 为空列表,且 force = true,表示强制返回空列表, // 否则路由结果为空的路由规则将自动失效 if (!result.isEmpty()) { return result; } else if (force) { logger.warn("The route result is empty and force execute ..."); return result; } } catch (Throwable t) { logger.error("Failed to execute condition router rule: ..."); } // 原样返回,此时 force = false,表示该条路由规则失效 return invokers; }
route 方法先是调用 matchWhen 对服务消费者进行匹配,如果匹配失败,直接返回 Invoker 列表。如果匹配成功,再对服务提供者进行匹配,匹配逻辑封装在了 matchThen 方法中。下面来看一下这两个方法的逻辑:
boolean matchWhen(URL url, Invocation invocation) {
// 服务消费者条件为 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 方法时传入。比如:
private List> route(List > invokers, String method) { Invocation invocation = new RpcInvocation(method, new Class>[0], new Object[0]); List routers = getRouters(); if (routers != null) { for (Router router : routers) { if (router.getUrl() != null) { // 注意第二个参数 invokers = router.route(invokers, getConsumerUrl(), invocation); } } } return invokers; }
上面这段代码来自 RegistryDirectory,第二个参数表示的是服务消费者 url。matchCondition 的 invocation 参数也是从这里传入的。
接下来再来看看 matchThen 向 matchCondition 方法传入的参数 [thenCondition, url, param, null]。第一个参数不用解释了。第二个和第三个参数来自 matchThen 方法的参数列表,这两个参数分别为服务提供者 url 和服务消费者 url。搞清楚这些参数来源后,接下来就可以分析 matchCondition 方法了。
private boolean matchCondition(Mapcondition, URL url, URL param, Invocation invocation) { // 将服务提供者或消费者 url 转成 Map Map sample = url.toMap(); boolean result = false; // 遍历 condition 列表 for (Map.Entry matchPair : condition.entrySet()) { // 获取匹配项名称,比如 host、method 等 String key = matchPair.getKey(); String samplevalue; // 如果 invocation 不为空,且 key 为 method(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 的值,分割线以下才是进行条件匹配。



