该篇幅分析RegistryDirectory.subscribe(URL url)方法
RegistryDirectory.subscribe(URL url)
public void subscribe(URL url) {
//consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&
//category=providers,configurators,routers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
//interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=24092&
//side=consumer×tamp=1573453550695
setConsumerUrl(url);
//ZookeeperRegistry的subscribe调用,这一步操作会建立对zk的节点目录providers、configurators、routers的监听
//如果这些zk的这些目录内容发生改变则会反馈到RegistryDirectory的notify方法!!!
//逻辑我就不用讲了吧,同服务暴露的流程一致!
registry.subscribe(url, this);
}
首先设置consumerUrl属性:consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer& category=providers,configurators,routers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3& interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=24092& side=consumer×tamp=1573453550695,调用注册中心subscribe(url,this)进行订阅,由于RegistryDirectory实现了NotifyListener接口,所以当routers、providers、configurators节点下的数据发生变化将会触发NotifyListener的notify(List<URL> urls)方法
FailbackRegistry.subscribe(URL url,NotifyListener)
@Override
public void subscribe(URL url, NotifyListener listener) {
//将当前url对应的Listener放入缓存集合
super.subscribe(url, listener);
//从failedSubscribed/failedUnsubscribed集合中删除该OverrideListener
removeFailedSubscribed(url, listener);
try {
// Sending a subscription request to the server side
//发送订阅请求到服务器端
doSubscribe(url, listener);
} catch (Exception e) {
Throwable t = e;
//获取该url对应的缓存url
List<URL> urls = getCacheUrls(url);
if (urls != null && !urls.isEmpty()) {
notify(url, listener, urls);
logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
} else {
// If the startup detection is opened, the Exception is thrown directly.
//如果开启了check = true,直接抛出异常
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true);
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
}
// Record a failed registration request to a failed list, retry regularly
addFailedSubscribed(url, listener);
}
}
该方法我们在服务暴露已经分析过了,无妨重新分析一遍加深印象
AbstractRegistry.subscribe(url,listener)
@Override
public void subscribe(URL url, NotifyListener listener) {
if (url == null) {
throw new IllegalArgumentException("subscribe url == null");
}
if (listener == null) {
throw new IllegalArgumentException("subscribe listener == null");
}
if (logger.isInfoEnabled()) {
logger.info("Subscribe: " + url);
}
//key为overrideSuscribeUrl类似如下格式
//listener
// provider://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&category=configurators&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo&pid=3720&side=provider×tamp=1571881716824
// consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&category=providers,configurators,routers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=15272&side=consumer×tamp=1573623575474
Set<NotifyListener> listeners = subscribed.get(url);
if (listeners == null) {
subscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
listeners = subscribed.get(url);
}
listeners.add(listener);
}
将url与List<NotifyListener>关系通过map维护起来
FailbackRegistry.removeFailedSubscribed(URL url,NotifyListener)
private void removeFailedSubscribed(URL url, NotifyListener listener) {
//从failedSubscribed/failedUnsubscribed中overrideSubscribeUrl所对应的监听器集合中删除overrideSubscribeListener实例
Set<NotifyListener> listeners = failedSubscribed.get(url);
if (listeners != null) {
listeners.remove(listener);
}
//
listeners = failedUnsubscribed.get(url);
if (listeners != null) {
listeners.remove(listener);
}
//从failedNotified中获取overrideSubscrbieUrl通知失败的map Map<NotifyListener, List<URL>>
//然后移除listener
Map<NotifyListener, List<URL>> notified = failedNotified.get(url);
if (notified != null) {
notified.remove(listener);
}
}
从failedSubscribed/failedUnsubscribed map集合中移除url(consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&category=providers,configurators,routers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=15272&side=consumer×tamp=1573623575474)对应的NotifyListener,从failedNotified map中移除url对应的Map<NotifyListener,List<URL>>的notifyListener对应的List<URL>
ZookeeperRegistry.doSubscribe(final URL url,final NotifyListener listener)
@Override
protected void doSubscribe(final URL url, final NotifyListener listener) {
try {
//订阅所有数据,监控中心的订阅
if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
//获取根目录
String root = toRootPath();
//获取url对应的监听器集合
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
//不存在,赋值新的
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
//获取listener的监听器
ChildListener zkListener = listeners.get(listener);
//zkListener为null说明需要创建新的
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
@Override
public void childChanged(String parentPath, List<String> currentChilds) {
//遍历当前节点,如果服务集合没有该节点,加入该节点,并订阅节点
for (String child : currentChilds) {
child = URL.decode(child);
if (!anyServices.contains(child)) {
//添加到服务接口集合中
anyServices.add(child);
//订阅URL
subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
}
});
//再次获取listener对应的ChildListener
zkListener = listeners.get(listener);
}
//创建root永久节点
zkClient.create(root, false);
List<String> services = zkClient.addChildListener(root, zkListener);
if (services != null && !services.isEmpty()) {
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
} else {
/**
* https://www.cnblogs.com/java-zhao/p/7632929.html 参考分析
*/
//处理消费者的订阅请求
List<URL> urls = new ArrayList<URL>();
//获取url的所有类型后
//url(overrideSubscribeUrl):provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?
// anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&
// interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider×tamp=1507643800076
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
@Override
public void childChanged(String parentPath, List<String> currentChilds) {
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
});
zkListener = listeners.get(listener);
}
//创建持久化节点创建持久化节点/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
zkClient.create(path, false);
//监听/dubbo/com.alibaba.dubbo.demo.DemoService/configurators节点
//返回path的子节点
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
//调用notify
notify(url, listener, urls);
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
首先调用通过toCategoriesPath(URL url)获取到zk需要监听的节点,然后创建url: :consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer& category=providers,configurators,routers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3& interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=22152& side=consumer×tamp=1573625041767对应的NotifyListener的ChildListener,ConcurrentMap<NotifyListener, ChildListener>,调用zkClient的create方法创建path:/dubbo/com.alibaba.dubbo.study.day01.xml.service.EchoService/providers的临时节点,调用zkClient的addChildListener方法将path与zkListener节点关联起来,这样子当path节点数据发生变化,zkListener就能立马监听到了,zkListener监听到了会调用childChanged方法,然后NotifyListener就能感知到节点数据变化,进而发生了动态订阅。此时的urls列表可能类似这样子
toCategoriesPath(URL url)
private String[] toCategoriesPath(URL url) {
String[] categories;
//代表所有类别
if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) {
categories = new String[]{Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY,
Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
} else {
//获取url的category属性,没有则给定provider类型
categories = url.getParameter(Constants.CATEGORY_KEY, new String[]{Constants.DEFAULT_CATEGORY});
}
String[] paths = new String[categories.length];
for (int i = 0; i < categories.length; i++) {
paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categories[i];///dubbo/com.alibaba.dubbo.demo.DemoService/configurators
}
return paths;
}
对于consumer://xxx来说该方法会返回的path列表有三个分别类似
- 1./dubbo/com.alibaba.dubbo.study.day01.xml.service.EchoService/providers
- 2./dubbo/com.alibaba.dubbo.study.day01.xml.service.EchoService/configurators
- 3./dubbo/com.alibaba.dubbo.study.day01.xml.service.EchoService/routers
AbstractZookeeperClient.addChildListener(String path, final ChildListener listener)
/**
* 根据path从ConcurrentMap<String, ConcurrentMap<ChildListener, TargetChildListener>> childListeners
* 获取ConcurrentMap<ChildListener, TargetChildListener>,没有就创建
*
* 根据ChildListener获取TargetChildListener,没有就创建,TargetChildListener是真正的监听path的子节点变化的监听器
* createTargetChildListener(String path, final ChildListener listener):创建一个真正的用来执行当path节点的子节点发生变化时的逻辑
*
* addTargetChildListener(path, targetListener):将刚刚创建出来的子节点监听器订阅path的变化,这样之后,path的子节点发生了变化时
* ,TargetChildListener才会执行相应的逻辑。而实际上TargetChildListener又会调用ChildListener的实现类的childChanged(String parentPath,
* List<String> currentChilds)方法,而该实现类,正好是ZookeeperRegistry中实现的匿名内部类,
* 在该匿名内部类的childChanged(String parentPath, List<String> currentChilds)方法中,
* 调用了ZookeeperRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
*
*/
@Override
public List<String> addChildListener(String path, final ChildListener listener) {
ConcurrentMap<ChildListener, TargetChildListener> listeners = childListeners.get(path);
if (listeners == null) {
childListeners.putIfAbsent(path, new ConcurrentHashMap<ChildListener, TargetChildListener>());
listeners = childListeners.get(path);
}
TargetChildListener targetListener = listeners.get(listener);
if (targetListener == null) {
listeners.putIfAbsent(listener, createTargetChildListener(path, listener));
targetListener = listeners.get(listener);
}
return addTargetChildListener(path, targetListener);
}
上面代码首先获取path:/dubbo/com.alibaba.dubbo.study.day01.xml.service.EchoService/providers的ChildListener的TargetChildListener,如果没有调用createTargetChildListener方法创建并加入到childListeners缓存起来ConcurrentHashMap<ChildListener, TargetChildListener>();TargetChildListener方法将会委托childListner实现节点数据变化进行处理的逻辑,最后调用addTargetChildListener真正的将zk自己的TargetChildListener监听path的数据并返回path下的子节点数据
ChildListener.childChaged(String parentPath, List<String> currentChilds)
@Override
public void childChanged(String parentPath, List<String> currentChilds) {
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
监听到path节点数据变化委托ZookeeperRegistry.notify方法进行处理
FailbackRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
@Override
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");
}
try {
doNotify(url, listener, urls);
} catch (Exception t) {
// Record a failed registration request to a failed list, retry regularly
// 将失败的通知请求记录到失败列表,定时重试
Map<NotifyListener, List<URL>> listeners = failedNotified.get(url);
if (listeners == null) {
failedNotified.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, List<URL>>());
listeners = failedNotified.get(url);
}
listeners.put(listener, urls);
logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
}
上述方法在异常中会将notify失败的放到failedNotified中重试
AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
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);
}
Map<String, List<URL>> result = new HashMap<String, List<URL>>();
//将匹配的urls按category分类保存到Map中
//key-categroyName value-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(overrideSubscribeUrl):provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?
// anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&
// interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider×tamp=1507643800076
//consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&
// category=providers,configurators,routers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
// interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=15140&
// side=consumer×tamp=1573626718233
//对应的通知过的URL集合 key-种类 value-List<URL> urls
Map<String, List<URL>> categoryNotified = notified.get(url);
if (categoryNotified == null) {
notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
categoryNotified = notified.get(url);
}
//遍历上述方法创建的map key-categroyName value-List<URL>集合
for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
//categroy
String category = entry.getKey();
//该分类对应的url
List<URL> categoryList = entry.getValue();
//放入到map中
categoryNotified.put(category, categoryList);
//保存到本地磁盘缓存中
saveProperties(url);
//调用传入的listener的notify方法(注意:这里调用的正是文章开头创建的overrideSubscribeListener实例的notify方法)
listener.notify(categoryList);
}
}
首先将urls(上图中的urls)按照category分类放到map中key-categroyName value-List<URL>,从notified中获取url:consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&category=providers,configurators,routers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=15140&side=consumer×tamp=1573626718233按照category(routers,configurators,providers)遍历按category分别进行RegistryDirectory.notify的调用!当然还有通知过的数据到notified中map中key-categroyName value-List<URL>(上图中的urls)到文件的逻辑。
RegistryDirectory.notify(List<URL> urls)
@Override
public synchronized void notify(List<URL> urls) {
//携带提供者元数据的url
List<URL> invokerUrls = new ArrayList<URL>();
//携带路由元数据的url
List<URL> routerUrls = new ArrayList<URL>();
//携带配置元数据的url
List<URL> configuratorUrls = new ArrayList<URL>();
//遍历routers、configurators、providers目录下的元数据配置
for (URL url : urls) {
String protocol = url.getProtocol();
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
//如果category类型为routers 或者url的协议为route
if (Constants.ROUTERS_CATEGORY.equals(category)
|| Constants.ROUTE_PROTOCOL.equals(protocol)) {
routerUrls.add(url);
//如果category类型为configurators 或者 协议类型为override
} else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
|| Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
configuratorUrls.add(url);
//如果category类型为providers
} 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());
}
}
//
//转换configuratorsUrl为List<Configurator>对象
if (configuratorUrls != null && !configuratorUrls.isEmpty()) {
this.configurators = toConfigurators(configuratorUrls);
}
// routers
//转换routerUrls对象为List<Router>
if (routerUrls != null && !routerUrls.isEmpty()) {
List<Router> routers = toRouters(routerUrls);
if (routers != null) { // null - do nothing
setRouters(routers);
}
}
//
List<Configurator> localConfigurators = this.configurators; // local reference
// merge override parameters
// 合并override的参数
this.overrideDirectoryUrl = directoryUrl;
if (localConfigurators != null && !localConfigurators.isEmpty()) {
for (Configurator configurator : localConfigurators) {
this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
}
}
// providers
refreshInvoker(invokerUrls);
}
将urls按照category的分类分别保存到三个集合中invokerUrls,routerUrls,configuratorUrls;将configuratorUrls转换为List<Configurator> configurators集合作为RegistryDirectory的成员变量属性,将routeUrls转换成List<Router> routers作为RegistryDirectory的routers的成员变量属性,使用configurators对overrdeDirectoryUrl:zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&check=false& dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService& methods=echo,addListener&pid=24924®ister.ip=169.254.22.149&side=consumer×tamp=1573453320189进行处理,最后调用 refreshInvoker 方法刷新 Invoker 列表
toConfigurators(List<URL> urls)
/**
* Convert override urls to map for use when re-refer.
* 将替代网址转换为映射以供重新引用时使用
* Send all rules every time, the urls will be reassembled and calculated
* 每次发送所有规则,网址将重新组合并计算
*
* @param urls Contract:
* </br>1.override://0.0.0.0/...( or override://ip:port...?anyhost=true)¶1=value1... 表示全局规则(所有提供者均生效)
* </br>2.override://ip:port...?anyhost=false 特殊规则(仅适用于特定提供者)
* </br>3.override:// 不支持规则...,需要由注册表本身进行计算。
* </br>4.override://0.0.0.0/ 没有参数意味着清除覆盖
* @return
*/
public static List<Configurator> toConfigurators(List<URL> urls) {
if (urls == null || urls.isEmpty()) {
return Collections.emptyList();
}
List<Configurator> configurators = new ArrayList<Configurator>(urls.size());
for (URL url : urls) {
//如果是empty://清空configurators
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
configurators.clear();
break;
}
//获取url中的所有参数
Map<String, String> override = new HashMap<String, String>(url.getParameters());
//The anyhost parameter of override may be added automatically, it can't change the judgement of changing url
//override://xxx的any属性可能会被自动添加,
//删除掉anyhost属性后,没有其他属性了
override.remove(Constants.ANYHOST_KEY);
if (override.size() == 0) {
//清空所有configurators,进入下一次循环
configurators.clear();
continue;
}
//添加
configurators.add(configuratorFactory.getConfigurator(url));
}
Collections.sort(configurators);
return configurators;
}
上述方法将override://xxx转换为configurators列表,遍历urls(override://xxx),如果是有empty://xxx则清空configurators配置列表并跳出循环意思就是清理掉所有的配置规则,获取url(override://xxx)中的所有parameters,如果url只有anyhost属性,清空configurators,进入下一轮循环,最后使用configuratorFactory.getConfigurator(url)创建Configurator对象(),ConfiguratoryFactory是dubbo的一个spi接口,在RegistryDirectory中其类型为:ConfiguratorFactory$Adaptive 代码如下
package com.alibaba.dubbo.rpc.cluster;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ConfiguratorFactory$Adaptive implements com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory {
public com.alibaba.dubbo.rpc.cluster.Configurator getConfigurator(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getProtocol();
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory extension = (com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory.class).getExtension(extName);
return extension.getConfigurator(arg0);
}
}
一般我们采用override://xxx进行配置,采用覆盖配置动态实现RPC的调用行为
OverrideConfiguratorFactory.java
public class OverrideConfiguratorFactory implements ConfiguratorFactory {
@Override
public Configurator getConfigurator(URL url) {
return new OverrideConfigurator(url);
}
}
一般情况下我们采用OverrideConfigurator对原有的配置进行覆盖,所以下文以OverrideConfigurator为重点内容。
AbsentConfiguratorFactory.java
public class AbsentConfiguratorFactory implements ConfiguratorFactory {
@Override
public Configurator getConfigurator(URL url) {
return new AbsentConfigurator(url);
}
}
OverrideConfigurator.java
- Configurator.java:配置providerUrl的抽象接口
public interface Configurator extends Comparable<Configurator> {
/**
* 获取配置url
* override://10.20.153.10/com.foo.BarService?category=configurators&dynamic=false&disbaled=true
* @return configurator url.
*/
URL getUrl();
/**
* 配置提供者url
* @param url - old provider url.
* @return new provider url.
*/
URL configure(URL url);
}
- AbstractConfigurator.java,提供了对providerUrl配置的抽象实现,并提供模板方法doConfigure(URL currentUrl, URL configUrl)留给子类决定去覆盖还是保留原有配置!
/**
* 携带配置的Url
* override://10.20.153.10/com.foo.BarService?category=configurators&dynamic=false&disbaled=true
*/
private final URL configuratorUrl;
public AbstractConfigurator(URL url) {
if (url == null) {
throw new IllegalArgumentException("configurator url == null");
}
this.configuratorUrl = url;
}
- OverrideConfigurator.java:具体的配置策略实现,主要是用于覆盖原有提供者的配置
public class OverrideConfigurator extends AbstractConfigurator {
public OverrideConfigurator(URL url) {
super(url);
}
@Override
public URL doConfigure(URL currentUrl, URL configUrl) {
return currentUrl.addParameters(configUrl.getParameters());
}
}
RegistryDirectory.toRouters(List<URL> urls)
private List<Router> toRouters(List<URL> urls) {
List<Router> routers = new ArrayList<Router>();
if (urls == null || urls.isEmpty()) {
return routers;
}
if (urls != null && !urls.isEmpty()) {
for (URL url : urls) {
//如果是empty:// 进入下一次循环
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
continue;
}
//获取router属性route://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule=host != 10.20.153.10,10.20.153.11
//支持scipt、condition、file三种类型
String routerType = url.getParameter(Constants.ROUTER_KEY);
if (routerType != null && routerType.length() > 0) {
url = url.setProtocol(routerType);
}
try {
//使用routerFactory创建Router并添加
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;
}
上述方法遍历urls,首先判断url是否empty://xxx是就跳过进入下一次循环,然后获取route的种类,dubbo目前支持file、condition、file三种类型route配置,最后使用RegistryDirectory.routerFactory,创建router实例,存入结果集合routers中。在RegistryDirectory中其类型为:ConfiguratorFactory$Adaptive 代码如下
package com.alibaba.dubbo.rpc.cluster;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class RouterFactory$Adaptive implements com.alibaba.dubbo.rpc.cluster.RouterFactory {
public com.alibaba.dubbo.rpc.cluster.Router getRouter(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getProtocol();
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.cluster.RouterFactory) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.cluster.RouterFactory extension = (com.alibaba.dubbo.rpc.cluster.RouterFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.RouterFactory.class).getExtension(extName);
return extension.getRouter(arg0);
}
}
ConditionRouterFactory.java
public class ConditionRouterFactory implements RouterFactory {
public static final String NAME = "condition";
@Override
public Router getRouter(URL url) {
return new ConditionRouter(url);
}
}
ScriptRouterFactory.java
public class ScriptRouterFactory implements RouterFactory {
public static final String NAME = "script";
@Override
public Router getRouter(URL url) {
return new ScriptRouter(url);
}
}
Dubbo给我们提供了三种不同的router实现,它们分别是ConditionRouter、ScriptRouter、TagRouter,我们用的最多的还是ConditionRouter实现
ConditionRouter.java
- Router.java:实现该接口主要负责按规则过滤出服务提供者的子集
public interface Router extends Comparable<Router>{
/**
*获取router url
* route://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule=" + URL.encode("host = 10.20.153.10 => host = 10.20.153.11
* @return url
*/
URL getUrl();
/**
* 路由.
* @param invokers
* @param url refer url
* @param invocation
* @return routed invokers
* @throws RpcException
*/
<T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
/**
* 路由器的优先级,用于对路由器进行排序。
*
* @return router's priority
*/
int getPriority();
- AbstractRouter.java:Router接口的抽象实现类
/**
* route://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule=" + URL.encode("host = 10.20.153.10 => host = 10.20.153.11
*/
protected URL url;
/**
* 路由优先级
*/
protected int priority;
- ConditionRouter.java
/**
*分组正则匹配
*/
private static Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)");
/**
* 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 false 。
*/
private final boolean force;
/**
* 消费者匹配条件集合,通过解析【条件表达式 rule 的 `=>` 之前半部分】
*/
private final Map<String, MatchPair> whenCondition;
/**
* 提供者地址列表的过滤条件,通过解析【条件表达式 rule 的 `=>` 之后半部分】
*/
private final Map<String, MatchPair> thenCondition;
public ConditionRouter(URL url) {
this.url = url;
//获取url中的priority属性
this.priority = url.getParameter(Constants.PRIORITY_KEY, DEFAULT_PRIORITY);
//获取force属性
this.force = url.getParameter(Constants.FORCE_KEY, false);
try {
//获取rule属性
String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
if (rule == null || rule.trim().length() == 0) {
throw new IllegalArgumentException("Illegal route rule!");
}
//替换rule字符串中的consumer.属性和provider.属性
rule = rule.replace("consumer.", "").replace("provider.", "");
int i = rule.indexOf("=>");
//分割消费者和提供者规则
String whenRule = i < 0 ? null : rule.substring(0, i).trim();
String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim();
Map<String, MatchPair> when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<String, MatchPair>() : parseRule(whenRule);
Map<String, MatchPair> then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule);
// 注意:应在业务级别上确定“何时条件”是否可以为空。
this.whenCondition = when;
this.thenCondition = then;
} catch (ParseException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
RegistryDirectory.refreshInvoker(List<URL> invokerUrls)
/**
* 将invokerURL列表转换为Invoker Map。 转换规则如下:
* 1.If URL has been converted to invoker, it is no longer re-referenced and obtained directly from the cache, and notice that any parameter changes in the URL will be re-referenced.
* 2.If the incoming invoker list is not empty, it means that it is the latest invoker list
* 3.If the list of incoming invokerUrl is empty, It means that the rule is only a override rule or a route rule, which needs to be re-contrasted to decide whether to re-reference.
*
* 1.如果已将URL转换为调用程序invoker,则将不再重新引用该URL并直接从缓存,并且请注意,URL中的任何参数更改都将被重新引用。
* 2.如果传入的调用者列表不为空,则表示它是最新的调用者列表
* 3.如果传入的invokerUrl列表为空,则表示该规则只是覆盖规则或路由规则,需要重新进行比较以决定是否重新引用。
* @param invokerUrls this parameter can't be null
*/
// TODO: 2017/8/31 FIXME The thread pool should be used to refresh the address, otherwise the task may be accumulated.
private void refreshInvoker(List<URL> invokerUrls) {
// invokerUrls 仅有一个元素,且 url 协议头为 empty,此时表示禁用所有服务
if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
//设置 forbidden 为 true 禁止访问
this.forbidden = true;
this.methodInvokerMap = null;
//销毁所有的invoker
destroyAllInvokers();
} else {
//设置允许访问标识
this.forbidden = false;
//将urlInvokerMap的值赋值给oldUrlInvokerMap
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;
// 如果invokerUrls为空,并且已缓存的invokerUrls不为空,将缓存中的invoker url添加到invokerUrls中,这里可以说明如果providers目录未发送变化
if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
//将invokeUrls加入到缓存中,方便比较
this.cachedInvokerUrls = new HashSet<URL>();
this.cachedInvokerUrls.addAll(invokerUrls);
}
if (invokerUrls.isEmpty()) {
return;
}
//将invokerUrls转换成为invoker
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
//将 newUrlInvokerMap 转成方法名到 Invoker 列表的映射
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);
// 如果newUrlInvokerMap为空打印错误日志并返回
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;
}
this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
this.urlInvokerMap = newUrlInvokerMap;
try {
destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
} catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
- 1.判断invokerUrls的size是否为1并且仅有的一个invokerUrl是empty协议,这种情况设置forbidden为true,代表不可用,methodInvokerMap为null,并且销毁RegistryDirectory中的urlInvokerMap Map<String, Invoker<T>>
- 2.如果invokerUrls为空将cachedInvokerUrls加入invokerUrls否则缓存invokerUrls到cachedInvokerUrls中
- 3.调用toInvokers方法,根据invokerUrls创建Invokers,并使用invokerUrl与invoker对应起来Map<String, Invoker<T>> newUrlInvokerMap
- 4.调用toMethodInvokers方法,将Map<String, Invoker<T>> newUrlInvokerMap转换成方法名称与List<Invoker>的map Map<String, List<Invoker<T>>> newMethodInvokerMap
- 5.根据是否为多组调用toMergeMethodInvokerMap方法处理并赋值到Map<String, List<Invoker<T>>> methodInvokerMap属性
- 6.销毁未被使用的Invokers
destroyAllInvokers()
private void destroyAllInvokers() {
//本地已经保存了的Map<String, Invoker<T>> urlInvokerMap
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
if (localUrlInvokerMap != null) {
for (Invoker<T> invoker : new ArrayList<Invoker<T>>(localUrlInvokerMap.values())) {
try {
//调用invoker的destroy方法
invoker.destroy();
} catch (Throwable t) {
logger.warn("Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
}
}
localUrlInvokerMap.clear();
}
methodInvokerMap = null;
}
遍历所有的invoker调用detroy方法
DubboInvoker.destroy()
@Override
public void destroy() {
// in order to avoid closing a client multiple times, a counter is used in case of connection per jvm, every
// time when client.close() is called, counter counts down once, and when counter reaches zero, client will be
// closed.
//为了避免多次关闭客户端,在每个jvm进行连接的情况下,每次调用client.close()时,
// 都会使用一个计数器,计数器递减一次,而当计数器达到零时,客户端将被关闭。
if (super.isDestroyed()) {
return;
} else {
// double check to avoid dup close
destroyLock.lock();
try {
if (super.isDestroyed()) {
return;
}
super.destroy();
if (invokers != null) {
invokers.remove(this);
}
for (ExchangeClient client : clients) {
try {
client.close(ConfigUtils.getServerShutdownTimeout());
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
} finally {
destroyLock.unlock();
}
}
}
调用exchangeClient的close方法关闭客户端,具体关闭流程不在本篇幅的范围内
toInvokers(List<URL> urls)
/**
* 将url转换为调用程序,如果已引用url,则不会重新引用。
* @param urls
* @return invokers
*/
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
if (urls == null || urls.isEmpty()) {
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<String>();
//获取服务消费端配置的协议
String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {
// If protocol is configured at the reference side, only the matching protocol is selected
// 如果在消费端配置了协议,则仅选择匹配的协议
if (queryProtocols != null && queryProtocols.length() > 0) {
boolean accept = false;
String[] acceptProtocols = queryProtocols.split(",");
for (String acceptProtocol : acceptProtocols) {
if (providerUrl.getProtocol().equals(acceptProtocol)) {
accept = true;
break;
}
}
// 若服务消费者协议头不被消费者所支持,则忽略当前 providerUrl
if (!accept) {
continue;
}
}
//忽略empty协议
if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
//获取消费端是否支持服务端的的协议,不支持打印错误日志
if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
+ ", supported protocol: " + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
continue;
}
//合并Url,即提供者的配置合并
URL url = mergeUrl(providerUrl);
String key = url.toFullString();
//忽略重复的Url
if (keys.contains(key)) {
continue;
}
keys.add(key);
// 将本地url与invoker的缓存 赋值给localUrlInvokerMap
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap;
//获取缓存中该url对应的invoker
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
//不存在与缓存中
if (invoker == null) {
try {
boolean enabled = true;
//如果url中配置了disable属性
if (url.hasParameter(Constants.DISABLED_KEY)) {
enabled = !url.getParameter(Constants.DISABLED_KEY, false);
} else {
enabled = url.getParameter(Constants.ENABLED_KEY, true);
}
//如果没有禁用 调用通过protocol.refer方法获取Invoker
if (enabled) {
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
}
} catch (Throwable t) {
logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
}
if (invoker != null) { // Put new invoker in cache
newUrlInvokerMap.put(key, invoker);
}
} else {
//加入缓存
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
- 1.首先是通过protocol过滤掉消费者不需要或者不支持的providerUrl(dubbo://xxxx)
- 2.调用mergeUrl方法进行providerUrl配置的合并Url的参数。 顺序为:override> -D>消费者>提供程序
- 3.通过keys Set<String>集合进行providerUrl的去重,如果providerUrl已经创建invoker了,进入下一轮循环
- 4.然后从缓存urlInvokerMap Map<String,Invoker<T>>去取,没取到则创建!然后加入到newUrlInvokerMap Map<String,Invoker>中,并返回,其中创建是需要我们重点关注的点,我们单独开个篇幅分析!。
mergeUrl(URL providerUrl)
/**
* 合并网址参数。 顺序为:override> -D>消费者>提供程序
* @param providerUrl
* @return
*/
private URL mergeUrl(URL providerUrl) {
//合并服务消费的queryMap到providerUrl:
//dubbo://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?addListener.1.callback=true&
//addListener.retries=2&anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&
//dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&
//methods=echo,addListener&pid=27968&side=provider×tamp=1573694769457
providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // Merge the consumer side parameters
//从configurators解析出来的override://xxx 配置用于覆盖providerUrl的配置!
List<Configurator> localConfigurators = this.configurators; // local reference
if (localConfigurators != null && !localConfigurators.isEmpty()) {
for (Configurator configurator : localConfigurators) {
providerUrl = configurator.configure(providerUrl);
}
}
//添加check属性到providerUrl中
providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false));
// directoryUrl和override的组合位于notify的末尾,此处无法处理
this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // Merge the provider side parameters
// 变换providerUrl的path
if ((providerUrl.getPath() == null || providerUrl.getPath().length() == 0)
&& "dubbo".equals(providerUrl.getProtocol())) { // Compatible version 1.0
//fix by tony.chenl DUBBO-44
String path = directoryUrl.getParameter(Constants.INTERFACE_KEY);
if (path != null) {
int i = path.indexOf('/');
if (i >= 0) {
path = path.substring(i + 1);
}
i = path.lastIndexOf(':');
if (i >= 0) {
path = path.substring(0, i);
}
providerUrl = providerUrl.setPath(path);
}
}
return providerUrl;
}
toMethodInvokers(Map<String, Invoker<T>> invokersMap)
/**
* Transform the invokers list into a mapping relationship with a method
* 得到 <methodName, Invoker 列表> 映射关系
* @param invokersMap Invoker Map
* @return Mapping relation between Invoker and method
*/
private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
//方法名 与 invoker的列表
Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
//invokersMap的非空判断
if (invokersMap != null && invokersMap.size() > 0) {
//遍历invokers
for (Invoker<T> invoker : invokersMap.values()) {
//获取url中的methods属性
String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
//如果存在methods属性
if (parameter != null && parameter.length() > 0) {
//使用,分割methods属性值
String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
if (methods != null && methods.length > 0) {
//遍历methods
for (String method : methods) {
//method方法名称不为null并且不是*
if (method != null && method.length() > 0
&& !Constants.ANY_VALUE.equals(method)) {
//根据方法名获取 Invoker 列表
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null) {
methodInvokers = new ArrayList<Invoker<T>>();
newMethodInvokerMap.put(method, methodInvokers);
}
//存储 Invoker 到列表中
methodInvokers.add(invoker);
}
}
}
}
invokersList.add(invoker);
}
}
//服务级别路由
List<Invoker<T>> newInvokersList = route(invokersList, null);
//存储 <*, newInvokersList> 映射关系,就是任何方法都支持的invoker列表
newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList);
//如果RegistryDirectory的serviceMethods属性不为空
if (serviceMethods != null && serviceMethods.length > 0) {
for (String method : serviceMethods) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null || methodInvokers.isEmpty()) {
methodInvokers = newInvokersList;
}
newMethodInvokerMap.put(method, route(methodInvokers, method));
}
}
// 排序,转成不可变列表
for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
Collections.sort(methodInvokers, InvokerComparator.getComparator());
newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
}
return Collections.unmodifiableMap(newMethodInvokerMap);
}
通过遍历Map<String,Invoker<T>> newUrlInvokerMap,得到invokerList,还有保存了key-methodName value-List<Invoker>的map集合newMethodInvokerMap Map<String, List<Invoker<T>>>,首先对invokerList进行route路由的List<Invoker>然后加入到newMethodInvokerMap中,然后判断RegistryDirectory.serviceMethods的属性是否为空,进行方法级别的路由骚操作后重新更换newMethodInvokerMap methodName对应的List<Invoker>,最后一步对每个methodName的List<Invoker>排序后修改为不可变集合后返回;关于路由参考:https://blog.csdn.net/qq_23536449/article/details/103054529
route(List<Invoker<T>> invokers, String method)
private List<Invoker<T>> route(List<Invoker<T>> invokers, String method) {
Invocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
//获取routers路由
List<Router> routers = getRouters();
//路由不为Null
if (routers != null) {
for (Router router : routers) {
// If router's url not null and is not route by runtime,we filter invokers here
//如果路由器的url不为null并且不在运行时路由,则在此处过滤调用程序
if (router.getUrl() != null && !router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) {
invokers = router.route(invokers, getConsumerUrl(), invocation);
}
}
}
return invokers;
}
关于路由做了什么事情,另起篇幅探讨。
toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap)
private Map<String, List<Invoker<T>>> toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap) {
Map<String, List<Invoker<T>>> result = new HashMap<String, List<Invoker<T>>>();
//遍历methodmap key为方法名称 value为List<Invoker>
for (Map.Entry<String, List<Invoker<T>>> entry : methodMap.entrySet()) {
String method = entry.getKey();
List<Invoker<T>> invokers = entry.getValue();
//key为group value为List<Invoker>
Map<String, List<Invoker<T>>> groupMap = new HashMap<String, List<Invoker<T>>>();
for (Invoker<T> invoker : invokers) {
String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, "");
List<Invoker<T>> groupInvokers = groupMap.get(group);
if (groupInvokers == null) {
groupInvokers = new ArrayList<Invoker<T>>();
groupMap.put(group, groupInvokers);
}
groupInvokers.add(invoker);
}
// 如果 groupMap 中仅包含一组键值对,此时直接取出该键值对的值即可
if (groupMap.size() == 1) {
result.put(method, groupMap.values().iterator().next());
// groupMap.size() > 1 成立,表示 groupMap 中包含多组键值对,比如:
// {
// "group1": [invoker1, invoker2, invoker3, ...],
// "group2": [invoker4, invoker5, invoker6, ...]
// }
} else if (groupMap.size() > 1) {
List<Invoker<T>> groupInvokers = new ArrayList<Invoker<T>>();
// 通过集群类合并每个分组对应的 Invoker 列表
for (List<Invoker<T>> groupList : groupMap.values()) {
groupInvokers.add(cluster.join(new StaticDirectory<T>(groupList)));
}
result.put(method, groupInvokers);
} else {
result.put(method, invokers);
}
}
return result;
}
遍历newMethodInvokerMap,对每个methodName的List<Invoker>处理,如果List<Invoker>只属于一个组那么不需要做什么,否则的话,使用cluster.join进行合并组处理这样子就得到了一个ClusterInvoker,然后更新methodName对应的List<Invoker>。Cluster是dubbo的一个SPI扩展接口Cluster$Adaptive
package com.alibaba.dubbo.rpc.cluster;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Cluster$Adaptive implements com.alibaba.dubbo.rpc.cluster.Cluster {
public com.alibaba.dubbo.rpc.Invoker join(com.alibaba.dubbo.rpc.cluster.Directory arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("cluster", "failover");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) name from url(" + url.toString() + ") use keys([cluster])");
com.alibaba.dubbo.rpc.cluster.Cluster extension = (com.alibaba.dubbo.rpc.cluster.Cluster) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.Cluster.class).getExtension(extName);
return extension.join(arg0);
}
}
限于篇幅太长,Cluster放在后面进行分析好了。
服务注册与动态发现机制原理和细节总结:
- 1.服务提供者暴露服务时,向服务注册中心注册自己,即在注册中心上创建/dubbo/service/providers/下创建个临时节点,服务提供者与注册中心保持长连接,一旦连接断开或者会话失效后,注册中心认为该服务提供者不可用。
- 2.服务消费者在启动时,向注册中心注册自己,即在注册中心上添加一个临时节点/dubbo/service/consumers/.
- 3.服务消费者订阅/dubbo/service/providers、configurators、consumers三个目录节点,如果这三个临时目录节点下内容发生改变,都会通知到消费者,根据目录节点下配置的元数据信息,重新引用Invoker实现。