Router 路由: 根据路由规则从多个 Invoker 中选出一个子集。
应用:AbstractDirectory 是所有目录服务实现的上层抽象, 它在 list 列举出所有 invokers 后,会在通过 Router 服务进行路由过滤。
1 路由接口定义
/** * Router. (SPI, Prototype, ThreadSafe) * * Routing * * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory) * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation) * @author chao.liuc */ public interface Router extends Comparable{ /** * get the router url. * * @return url */ URL getUrl(); /** * route. * * @param invokers * @param url refer url * @param invocation * @return routed invokers * @throws RpcException */ <T> List T>> route(List T>> invokers, URL url, Invocation invocation) throws RpcException; }
2 应用1:com.alibaba.dubbo.rpc.cluster.directory.AbstractDirectory#list
public ListT>> list(Invocation invocation) throws RpcException { if (destroyed) throw new RpcException("Directory already destroyed .url: " + getUrl()); List T>> invokers = doList(invocation); return route(invokers, invocation); } private List T>> route(List T>> invokers, Invocation invocation) { // local reference List localRouters = this.routers; if (localRouters == null || localRouters.isEmpty()) return invokers; for (Router router : localRouters) { try { if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) { invokers = router.route(invokers, getConsumerUrl(), invocation);//调用具体路由规则 } } catch (Throwable t) { logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t); } } return invokers; }
3 应用2 com.alibaba.dubbo.registry.integration.RegistryDirectory#route
//服务发现的时候先路由一次 private ListT>> route(List T>> invokers, String method) { Invocation invocation = new RpcInvocation(method, new Class>[0], new Object[0]); List routers = getRouters(); if (routers == null) return invokers; for (Router router : routers) { if (router.getUrl() != null && !router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) { invokers = router.route(invokers, getConsumerUrl(), invocation); } } return invokers; }
4 调用源:
private void executeRoute(Map, List T>>> method2Invokers) { if (serviceMethods == null || serviceMethods.length == 0) return; for (String method : serviceMethods) { List T>> methodInvokers = method2Invokers.get(method); if (methodInvokers == null || methodInvokers.size() == 0) { methodInvokers = method2Invokers.get(Constants.ANY_VALUE); } method2Invokers.put(method, route(methodInvokers, method));//这里调用rute } }
5 调用源:
/** * 将invokers列表转成与方法的映射关系 * * @param invokersMap Invoker列表 * @return Invoker与方法的映射关系 */ private Map, List T>>> toMethodInvokers(Map , Invoker<T>> invokersMap) { Map , List T>>> newMethodInvokerMap = groupByMethod(invokersMap.values()); executeRoute(newMethodInvokerMap); // sort and unmodifiable for (String method : new HashSet (newMethodInvokerMap.keySet())) { List T>> methodInvokers = newMethodInvokerMap.get(method); Collections.sort(methodInvokers, InvokerComparator.getComparator()); newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers)); } return Collections.unmodifiableMap(newMethodInvokerMap); }
6 调用源:
/** * 根据invokerURL列表转换为invoker列表。转换规则如下: * 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用 * 2.如果传入的invoker列表不为空,则表示最新的invoker列表 * 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。 * * @param invokerUrls 传入的参数不能为null */ private void refreshInvoker(ListinvokerUrls) { if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { this.forbidden = true; // 禁止访问 overrideDirectoryUrl = overrideDirectoryUrl.addParameters(Constants.INVOKER_INSIDE_INVOKERS_KEY, "", Constants.INVOKER_INSIDE_INVOKER_COUNT_KEY, "0"); this.methodInvokerMap = null; // 置空列表 destroyAllInvokers(); // 关闭所有Invoker } else { this.forbidden = false; // 允许访问 Map, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference if (invokerUrls.size() == 0 && this.lastInvokerUrls != null) { invokerUrls.addAll(this.lastInvokerUrls); } else { this.lastInvokerUrls = new HashSet () ; this.lastInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比 } if (invokerUrls.size() == 0) return; Map, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// 将URL列表转成Invoker列表 // state change //如果计算错误,则不进行处理. //比如所有provider都标记为下线状态,这样其实最后一个provider是下不了的 if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) { logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString())); return; } Map , List T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表 this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap; this.urlInvokerMap = newUrlInvokerMap; try { destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // 关闭未使用的Invoker } catch (Exception e) { logger.warn("destroyUnusedInvokers error. ", e); } List invokerUrlString = new ArrayList() ; for (URL invoker : invokerUrls) { invokerUrlString.add(invoker.toString()); } overrideDirectoryUrl = overrideDirectoryUrl.addParameters( Constants.INVOKER_INSIDE_INVOKERS_KEY, URL.encode(CollectionUtils.join(invokerUrlString, ";")), Constants.INVOKER_INSIDE_INVOKER_COUNT_KEY, String.valueOf(invokerUrls.size())); } }
7 最终就是回调函数 com.alibaba.dubbo.registry.integration.RegistryDirectory#notify
public synchronized void notify(Listurls) { 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()); } } // routers if (routerUrls.size() > 0) { List routers = toRouters(routerUrls); if (routers != null) { // null - do nothing setRouters(routers); } } // configurators if (configuratorUrls.size() > 0) { this.configurators = toConfigurators(configuratorUrls); } 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); } } if (configuratorUrls.size() > 0) { logger.info("unconfigured directory url without provider params: " + this.directoryUrl + ", configured directory url without provider params: " + this.overrideDirectoryUrl); } // providers refreshInvoker(invokerUrls); }