2019独角兽企业重金招聘Python工程师标准>>>
这回说说,dubbo路由特性,dubbo的路由干的事,就是一个请求过来,
dubbo依据配置的路由规则,计算出哪些提供者可以提供这次的请求服务。
所以,它的优先级是在集群容错策略和负载均衡策略之前的。
即先有路由规则遴选出符合条件的服务提供者
然后,再在这些服务提供者之中应用负载均衡,集群容错策略。
流程图大概是如下图:
可以通过代码验证上面的逻辑。
看AbstractClusterInvoker类invoke方法,服务调用的入口方法:
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
LoadBalance loadbalance;
//list方法
//会调用directory的list方法。
//其实是AbstractDirectory的list方法,这个方法里就是利用路由规则(如果有),从所有
//提供者中,遴选出符合规则的提供者s.
//接下里才是集群容错和负载均衡。
//可以发现每次调用这些过程都重新下走一遍这个逻辑
List> invokers = list(invocation);
if (invokers != null && invokers.size() > 0) {
//从url通过key "loadbalance" 取不到值,就取默认random随机策略
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(invocation.getMethodName(), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
} else {
//取默认random随机策略
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
}
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
list方法的具体实现,在AbstractDirectory类里
/***
* 落地路由规则
* @param invocation
* @return
* @throws RpcException
*/
public List> list(Invocation invocation) throws RpcException {
if (destroyed) {
throw new RpcException("Directory already destroyed .url: " + getUrl());
}
//获取所有的提供者
List> invokers = doList(invocation);
//本地路由规则,这个其实已通过setRouters方法设置好。什么时候设置的,稍后看看
List localRouters = this.routers; // local reference
if (localRouters != null && localRouters.size() > 0) {
for (Router router : localRouters) {
try {
if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
//依次通过路由器的滤路由规则,最后返回invokers
invokers = router.route(invokers, getConsumerUrl(), invocation);
}
} catch (Throwable t) {
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
}
}
}
return invokers;
}
路由器在哪里设置的?还在AbstractDirectory类里setRouters方法
/***
* 设置路由器
* @param routers
*/
protected void setRouters(List routers) {
// copy list
routers = routers == null ? new ArrayList() : new ArrayList(routers);
//append url router
//获取路由key
//这里感觉是从consumer端配置router的路由规则,但是consumer没有router配置的地方???
//官方文档给出 配置方式,但是并没有实现。
String routerkey = url.getParameter(Constants.ROUTER_KEY);
if (routerkey != null && routerkey.length() > 0) {
//根据routerkey获取三种路由工厂的其中一种
RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
//再由路由工厂获取具体类型路由器
routers.add(routerFactory.getRouter(url));
}
// append mock invoker selector
// 追加mock invoker 路由器,
routers.add(new MockInvokersSelector());
//排序后MockInvokersSelector会放在最后
//MockInvokersSelector路由器,是dubbo对mock调用支持的一部分,稍后看下源码
Collections.sort(routers);
this.routers = routers;
}
setRouters在哪里调用的?在AbstractDirectory子类。
RegistryDirectory类,可以看到notify里有,notify是注册中心通知consumer回调的方法
public synchronized void notify(List urls) {
List invokerUrls = new ArrayList();
List routerUrls = new ArrayList();
List configuratorUrls = new ArrayList();
for (URL url : urls) {
String protocol = url.getProtocol();
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
if (Constants.ROUTERS_CATEGORY.equals(category)
|| Constants.ROUTE_PROTOCOL.equals(protocol)) {
routerUrls.add(url);
} else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
|| Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
configuratorUrls.add(url);
} else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
invokerUrls.add(url);
} else {
logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
}
}
// configurators
if (configuratorUrls != null && configuratorUrls.size() > 0) {
this.configurators = toConfigurators(configuratorUrls);
}
// routers
if (routerUrls != null && routerUrls.size() > 0) {
//把路由配置,装换成路由器实例
List routers = toRouters(routerUrls);
if (routers != null) { // null - do nothing
//设置了路由。
setRouters(routers);
}
}
List localConfigurators = this.configurators; // local reference
// 合并override参数
this.overrideDirectoryUrl = directoryUrl;
if (localConfigurators != null && localConfigurators.size() > 0) {
for (Configurator configurator : localConfigurators) {
this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
}
}
// providers
refreshInvoker(invokerUrls);
}
这样就保证配置可以从注册中心下发到调用方。
可以看到,这里有个 toRouters(routerUrls)方法把路由配置转换成路由器实例
/**
* 把路由配置转化成具体路由器
* @param urls
* @return null : no routers ,do nothing
* else :routers list
*/
private List toRouters(List urls) {
List routers = new ArrayList();
if (urls == null || urls.size() < 1) {
return routers;
}
if (urls != null && urls.size() > 0) {
for (URL url : urls) {
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
continue;
}
String routerType = url.getParameter(Constants.ROUTER_KEY);
if (routerType != null && routerType.length() > 0) {
//设置url的protocol为路由类型
url = url.setProtocol(routerType);
}
try {
//根据routerType获取对应的路由器工厂,然后获取具体路由器。然后放入路由器集合
//dubbo默认实现有file,script,condition三种类型工厂对应三种路由类型
//这些都是在RouterFactory$Adpative类完成适配的,RouterFactory$Adpative类是dubbo spi机制动态编码,编译生成的
Router router = routerFactory.getRouter(url);
if (!routers.contains(router))
routers.add(router);
} catch (Throwable t) {
logger.error("convert router url to router error, url: " + url, t);
}
}
}
return routers;
}
下一次,可以说说,三种路由的具体实现和配置方法。