AbstractRegistry类的notify方法
/** * 通知监听器,URL 变化结果。 * * 数据流向 `urls` => {@link #notified} => {@link #properties} => {@link #file} * * @param url 消费者 URL * @param listener 监听器 * @param urls 通知的 URL 变化结果(全量数据) */ protected void notify(URL url, NotifyListener listener, List<URL> urls) { if (url == null) { throw new IllegalArgumentException("notify url == null"); } if (listener == null) { throw new IllegalArgumentException("notify listener == null"); } if ((urls == null || urls.isEmpty()) && !Constants.ANY_VALUE.equals(url.getServiceInterface())) { logger.warn("Ignore empty notify urls for subscribe url " + url); return; } if (logger.isInfoEnabled()) { logger.info("Notify urls for subscribe url " + url + ", urls: " + urls); } // 将 `urls` 按照 `url.parameter.category` 分类,添加到集合 // 注意,特殊情况,使用 curator 连接 Zookeeper 时,若是服务消费者,连接断开,会出现 category=providers,configurations,routes Map<String, List<URL>> result = new HashMap<String, List<URL>>(); for (URL u : urls) { if (UrlUtils.isMatch(url, u)) { String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); List<URL> categoryList = result.get(category); if (categoryList == null) { categoryList = new ArrayList<URL>(); result.put(category, categoryList); } categoryList.add(u); } } if (result.size() == 0) { return; } // 获得消费者 URL 对应的在 `notified` 中,通知的 URL 变化结果(全量数据) Map<String, List<URL>> categoryNotified = notified.get(url); if (categoryNotified == null) { notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>()); categoryNotified = notified.get(url); } // 【按照分类循环】处理通知的 URL 变化结果(全量数据) for (Map.Entry<String, List<URL>> entry : result.entrySet()) { String category = entry.getKey(); List<URL> categoryList = entry.getValue(); // 覆盖到 `notified` // 当某个分类的数据为空时,会依然有 urls 。其中 `urls[0].protocol = empty` ,通过这样的方式,处理所有服务提供者为空的情况。 categoryNotified.put(category, categoryList); // 保存到文件 saveProperties(url); // 通知监听器 listener.notify(categoryList); } }
/** * 通知监听器 */ public interface NotifyListener { /** * 当收到服务变更通知时触发。 * <p> * 通知需处理契约:<br> * 1. 总是以服务接口和数据类型为维度全量通知,即不会通知一个服务的同类型的部分数据,用户不需要对比上一次通知结果。<br> * 2. 订阅时的第一次通知,必须是一个服务的所有类型数据的全量通知。<br> * 3. 中途变更时,允许不同类型的数据分开通知,比如:providers, consumers, routers, overrides,允许只通知其中一种类型,但该类型的数据必须是全量的,不是增量的。<br> * 4. 如果一种类型的数据为空,需通知一个empty协议并带category参数的标识性URL数据。<br> * 5. 通知者(即注册中心实现)需保证通知的顺序,比如:单线程推送,队列串行化,带版本对比。<br> * * @param urls 已注册信息列表,总不为空,含义同{@link com.alibaba.dubbo.registry.RegistryService#lookup(URL)}的返回值。 */ void notify(List<URL> urls); }
package com.alibaba.dubbo.registry.integration; /** * RegistryDirectory * * 基于注册中心的 Directory 实现类 */ public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener { /** * [url]与[服务提供者 Invoker 集合]的映射缓存 */ // Map<url, Invoker> cache service url to invoker mapping. private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference /** * [方法名]与[服务提供者 Invoker 集合]的映射缓存 */ // Map<methodName, Invoker> cache service method to invokers mapping. private volatile Map<String, List<Invoker<T>>> methodInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference @Override public synchronized void notify(List<URL> urls) { // 根据 URL 的分类或协议,分组成三个集合 。 List<URL> invokerUrls = new ArrayList<URL>(); // 服务提供者 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()); } } // 处理配置规则 URL 集合 // configurators if (!configuratorUrls.isEmpty()) { this.configurators = toConfigurators(configuratorUrls); } // 处理路由规则 URL 集合 // routers if (!routerUrls.isEmpty()) { List<Router> routers = toRouters(routerUrls); if (routers != null) { // null - do nothing setRouters(routers); } } // 合并配置规则,到 `directoryUrl` 中,形成 `overrideDirectoryUrl` 变量。 List<Configurator> localConfigurators = this.configurators; // local reference // merge override parameters this.overrideDirectoryUrl = directoryUrl; if (localConfigurators != null && !localConfigurators.isEmpty()) { for (Configurator configurator : localConfigurators) { this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } // 处理服务提供者 URL 集合 // providers 这里会把信息更新到methodInvokerMap中,在this.doList()方法中,会用到此变量,会在AbstractDirectory.doList调用 refreshInvoker(invokerUrls); } @Override public List<Invoker<T>> doList(Invocation invocation) { if (forbidden) { // 1. No service provider 2. Service providers are disabled throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist)."); } List<Invoker<T>> invokers = null; Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference // 获得 Invoker 集合 if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) { // 获得方法名、方法参数 String methodName = RpcUtils.getMethodName(invocation); Object[] args = RpcUtils.getArguments(invocation); // 【第一】可根据第一个参数枚举路由 if (args != null && args.length > 0 && args[0] != null && (args[0] instanceof String || args[0].getClass().isEnum())) { // invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // The routing can be enumerated according to the first parameter invokers = localMethodInvokerMap.get(methodName + args[0]); // The routing can be enumerated according to the first parameter } // 【第二】根据方法名获得 Invoker 集合 if (invokers == null) { invokers = localMethodInvokerMap.get(methodName); } // 【第三】使用全量 Invoker 集合。例如,`#$echo(name)` ,回声方法 if (invokers == null) { invokers = localMethodInvokerMap.get(Constants.ANY_VALUE); } // 【第四】使用 `methodInvokerMap` 第一个 Invoker 集合。防御性编程。 if (invokers == null) { Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator(); if (iterator.hasNext()) { invokers = iterator.next(); } } } return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers; } }
看到是那里更新服务提供者的了把,然后消费者调用是会调用doList获取最新的服务提供者列表的说以你懂得:
消费者B ==> 提供者A会经历下面的链路
InvokerInvocationHandler.invoke==>MigrationInvoker.invoke==>MockClusterInvoker.invoke==>AbstractCluster.invoke
==>ClusterInterceptor.intercept==>AbstractClusterInvoker.invoke
==>AbstractClusterInvoker.list==>AbstractDirectory.list==>RegistryDirectory.doList