下面我们来分析一下Dubbo的Router机制。Dubbo的路由规则就是根据路由规则从多个Invoker中选出一个子集AbstractDirectory是所有目录服务实现的上层抽象, 它在list列举出所有invokers后,会在通过Router服务进行路由过滤。先来看一下AbstractDirectory的setRouters方法
protected void setRouters(List routers) {
// copy list // 复制 routers ,因为下面要修改
routers = routers == null ? new ArrayList() : new ArrayList(routers);
// append url router
// 拼接 `url` 中,配置的路由规则
String routerkey = url.getParameter(Constants.ROUTER_KEY);
if (routerkey != null && routerkey.length() > 0) {
RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
routers.add(routerFactory.getRouter(url));
}
// append mock invoker selector
routers.add(new MockInvokersSelector());
// 排序
Collections.sort(routers);
// 赋值给属性
this.routers = routers;
}
再看一下list方法
public List> list(Invocation invocation) throws RpcException {
if (destroyed) {
throw new RpcException("Directory already destroyed .url: " + getUrl());
}
// 获得所有 Invoker 集合
List> invokers = doList(invocation);
// 根据路由规则,筛选 Invoker 集合
List localRouters = this.routers; // local reference 本地引用,避免并发问题
if (localRouters != null && !localRouters.isEmpty()) {
for (Router router : localRouters) {
try {
if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) {
invokers = router.route(invokers, getConsumerUrl(), invocation);
}
} catch (Throwable t) {
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
}
}
}
return invokers;
}
Router由RouterFactory工厂类生产,先来看一下ConditionRouterFactory
public Router getRouter(URL url) {
return new ConditionRouter(url);
}
这个类生产ConditionRouter,这个类是条件路由实现类
public List> route(List> invokers, URL url, Invocation invocation) throws RpcException {
// 为空,直接返回空 Invoker 集合
if (invokers == null || invokers.isEmpty()) {
return invokers;
}
try {
// 不匹配 `whenCondition` ,直接返回 `invokers` 集合,因为不需要走 `whenThen` 的匹配
if (!matchWhen(url, invocation)) {
return invokers;
}
List> result = new ArrayList>();
// `whenThen` 为空,则返回空 Invoker 集合
if (thenCondition == null) {
logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
return result;
}
// 使用 `whenThen` ,匹配 `invokers` 集合。若符合,添加到 `result` 中
for (Invoker invoker : invokers) {
if (matchThen(invoker.getUrl(), url)) {
result.add(invoker);
}
}
// 若 `result` 非空,返回它
if (!result.isEmpty()) {
return result;
// 如果 `force=true` ,代表强制执行,返回空 Invoker 集合
} else if (force) {
logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(Constants.RULE_KEY));
return result;
}
} catch (Throwable t) {
logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
}
// 如果 `force=false` ,代表不强制执行,返回 `invokers` 集合,即忽略路由规则
return invokers;
}
再看一下ScriptRouterFactory这个类
public Router getRouter(URL url) {
return new ScriptRouter(url);
}
这个类生产ScriptRouter,这个类是基于脚本的路由实现类
public List> route(List> invokers, URL url, Invocation invocation) throws RpcException {
try {
// 执行脚本
List> invokersCopy = new ArrayList>(invokers);
Compilable compilable = (Compilable) engine;
Bindings bindings = engine.createBindings();
bindings.put("invokers", invokersCopy);
bindings.put("invocation", invocation);
bindings.put("context", RpcContext.getContext());
CompiledScript function = compilable.compile(rule); // 编译
Object obj = function.eval(bindings); // 执行
// 根据结果类型,转换成 (List> 类型返回
if (obj instanceof Invoker[]) {
invokersCopy = Arrays.asList((Invoker[]) obj);
} else if (obj instanceof Object[]) {
invokersCopy = new ArrayList>();
for (Object inv : (Object[]) obj) {
invokersCopy.add((Invoker) inv);
}
} else {
invokersCopy = (List>) obj;
}
return invokersCopy;
} catch (ScriptException e) {
// 发生异常,忽略路由规则,返回全 `invokers` 集合
// fail then ignore rule .invokers.
logger.error("route error , rule has been ignored. rule: " + rule + ", method:" + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
return invokers;
}
}
在服务调用的时候,会根据Route集合过滤生产的Invoker,然后进行服务的调用。
Dubbo的Router实现就分析到这里了。