这回说说,dubbo路由特性,dubbo的路由干的事,就是一个请求过来,
dubbo依据配置的路由规则,计算出哪些提供者能够提供此次的请求服务。
因此,它的优先级是在集群容错策略和负载均衡策略以前的。
即先有路由规则遴选出符合条件的服务提供者
而后,再在这些服务提供者之中应用负载均衡,集群容错策略。
流程图大概是以下图:java
能够经过代码验证上面的逻辑。
看AbstractClusterInvoker类invoke方法,服务调用的入口方法:app
public Result invoke(final Invocation invocation) throws RpcException { checkWhetherDestroyed(); LoadBalance loadbalance; //list方法 //会调用directory的list方法。 //实际上是AbstractDirectory的list方法,这个方法里就是利用路由规则(若是有),从全部 //提供者中,遴选出符合规则的提供者s. //接下里才是集群容错和负载均衡。 //能够发现每次调用这些过程都从新下走一遍这个逻辑 List<Invoker<T>> 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<Invoker<T>> list(Invocation invocation) throws RpcException { if (destroyed) { throw new RpcException("Directory already destroyed .url: " + getUrl()); } //获取全部的提供者 List<Invoker<T>> invokers = doList(invocation); //本地路由规则,这个其实已经过setRouters方法设置好。何时设置的,稍后看看 List<Router> 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方法dom
/*** * 设置路由器 * @param routers */ protected void setRouters(List<Router> routers) { // copy list routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers); //append url router //获取路由key //这里感受是从consumer端配置router的路由规则,可是consumer没有router配置的地方??? //官方文档给出<dubbo:protocol router="xxx" />配置方式,可是并无实现。 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回调的方法ide
public synchronized void notify(List<URL> urls) { List<URL> invokerUrls = new ArrayList<URL>(); List<URL> routerUrls = new ArrayList<URL>(); List<URL> configuratorUrls = new ArrayList<URL>(); 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<Router> routers = toRouters(routerUrls); if (routers != null) { // null - do nothing //设置了路由。 setRouters(routers); } } List<Configurator> 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)方法把路由配置转换成路由器实例
this
/** * 把路由配置转化成具体路由器 * @param urls * @return null : no routers ,do nothing * else :routers list */ private List<Router> toRouters(List<URL> urls) { List<Router> routers = new ArrayList<Router>(); 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; }
下一次,能够说说,三种路由的具体实现和配置方法。编码