本系列主要参考官网文档、芋道源码的源码解读和《深入理解Apache Dubbo与实战》一书。Dubbo版本为2.6.1。
文章内容顺序:
1.什么是Router?
- Router的UML图
3.Router是什么时候注入进来的?
- 3.1 AbstractDirectory#setRouters
- 3.2 RouterFactory
- 3.3 ConditionRouterFactory
- 3.4 FileRouterFactory
- Router接口
- 4.1ConditionRouter
- 4.2其他两个Router实现类
1.什么是Router?
在上一篇Dubbo集群容错——Directory中,可以看到在
AbstractDirectory#list方法中,服务目录在获取可用 Invoker 列表后,会通过 Router 进行服务路由,筛选出符合路由规则的服务提供者。
服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者。
注:本文仅就简单分析下RouterFactory,对于Router的实现自认为写的没有官方详细(官方中只有ConditionRouter),对于Router实现部分请参考服务路由
2. Router的UML图
Router相关类如下图所示:
image.png
3.Router是什么时候注入进来的?
前文提到,在Directory篇中,服务目录在获取可用 Invoker 列表后,会通过 Router 进行服务路由,那么这个Router对象是怎么设置进来的呢?一起来看一下
3.1 AbstractDirectory#setRouters
image.png
可以看到在
AbstractDirectory#setRouters就用SPI机制加载了routerFactory,调用了其getRouter,通过解析url来得到routers。接下里就来看看RouterFactory。
3.2 RouterFactory
@SPI
public interface RouterFactory {
/**
* Create router.
*
* 创建 Router 对象
*
* @param url
* @return router
*/
@Adaptive("protocol")
Router getRouter(URL url);
}
Dubbo SPI 拓展点,无默认值。接口方法用来获得 Router 对象。
来看看他的实现
3.3 ConditionRouterFactory
public class ConditionRouterFactory implements RouterFactory {
public static final String NAME = "condition";
@Override
public Router getRouter(URL url) {
return new ConditionRouter(url);
}
}
对应 Router 实现类为 ConditionRouter 。
ScriptRouterFactory也是一样,直接返回了一个 new ScriptRouter(url),就不贴代码了。
FileRouterFactory有所不同。
3.4 FileRouterFactory
/**
* 基于文件读取路由规则,创建对应的 Router 实现类的对象
*/
public class FileRouterFactory implements RouterFactory {
public static final String NAME = "file";
/**
* RouterFactory$Adaptive 对象
*/
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>
// 获得 router 配置项,默认为 script
String protocol = url.getParameter(Constants.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();
if (path != null) {
int i = path.lastIndexOf('.');
if (i > 0) {
type = path.substring(i + 1);
}
}
// 读取规则内容
String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
// 创建路由规则 URL
boolean runtime = url.getParameter(Constants.RUNTIME_KEY, false);
URL script = url.setProtocol(protocol).addParameter(Constants.TYPE_KEY, type)
.addParameter(Constants.RUNTIME_KEY, runtime)
.addParameterAndEncoded(Constants.RULE_KEY, rule);
// 通过 Dubbo SPI Adaptive 机制,获得 Router 对象
return routerFactory.getRouter(script);
} catch (IOException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
这个方法最后仍然是需要调用其他两个
RouterFactory实现来读取文件,默认为ScriptRouterFactory。
4.Router接口
接下来看一下Router接口
public interface Router extends Comparable<Router> {
/**
* get the router url.
* <p>
* 路由规则 URL
*
* @return url
*/
URL getUrl();
/**
* route.
*
* 路由,筛选匹配的 Invoker 集合
*
* @param invokers Invoker 集合
* @param url refer url
* @param invocation
* @return routed invokers 路由后的 Invoker 集合
* @throws RpcException
*/
<T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
一个 Router 对象,对应一条路由规则。
注意,实现了Comparable接口,用于排序,路由规则优先级越大越靠前执行,可不填,缺省为 0
4.1 ConditionRouter
ConditionRouter的代码解读还是官方比较全面,还有详细的例子:服务路由
- 这里总结一下
ConditionRouter的代码逻辑:
当创建ConditionRouter对象时,ConditionRouter构造方法先是对路由规则做预处理,然后调用parseRule方法分别对服务提供者和消费者规则进行解析。
route方法先是调用matchWhen对服务消费者进行匹配,如果匹配失败,直接返回Invoker列表。如果匹配成功,再对服务提供者进行匹配,匹配逻辑封装在了matchThen方法中。matchWhen与matchThen的区别仅在于,调用matchCondition时传的url不同。matchWhen对应服务提供者url,matchThen对应服务消费者url
4.2其他两个Router实现类
对于ScriptRouter,与ConditionRouter的区别就是从配置文件读还是从脚本文件读。
而MockInvokerSeclector我们留待到Mock的时候再来详细讲解。








网友评论