上篇我们介绍了provider服务暴露源码,地址如下
Dubbo源码解析-Provider服务暴露Export源码解析_dubbo exporter-CSDN博客
本文主要针Dubbo服务端注册中心节点,实现动态配置变更原理,从dubbo源码角度进行解析。
Dubbo 服务端动态配置原理比较简单,也是面试过程中比较常问的技术问题,大家可以好好仔细读一下本文。有疑问欢迎留言。
接着说明,读Dubbo源码最好是先对Spring源码有一定的了解。如果大家需要,我也可以针对Spring框架做一系列源码的解读专栏。
不过不用担心,如果需要Spring的源码知识,文章中也会进行Spring源码铺垫介绍的。
如果内容中有没描述清楚的,或者大家在阅读源代码有疑问的,欢迎留言,看到就会及时回复。
为了更清楚的分析解释源码,源代码中部分不重要的内容可能会删减,保留重要内容方便大家理解。
我们知道,Dubbo对每个服务的配置,会写入到服务节点对应的configurators节点,在服务端服务启动后,会注册监听时间,来监听/dubbo/com.*.*/configurators/节点变更。从而实现动态刷新本地服务配置功能。具体过程我们接下来详细介绍一下。
主要内容
- 服务端什么时候订阅的
- 服务端订阅事件做了什么
服务端如何订阅的
Dubbo对每个服务的配置,会写入到服务节点对应的configurators节点,在服务端服务启动后,会注册监听时间,来监听/dubbo/com.*.*/configurators/节点变更。具体流程可以参考Dubbo源码解析-Provider服务暴露Export源码解析_dubbo exporter-CSDN博客
服务端订阅事件
服务端在启动的过程中建立了一个notify的映射关系CuratorWatch.press->ChildListener.childChange()->AbstractRegistry.notify()。完成override事件对主机属性的覆盖和对客户端代理的生成。
流程概述:
- verride协议中修改或者添加的属性合并到originUrl中,生成新的URL
- 比较新的URL和当前URL是否相同
- 不相同则重新完成服务暴露:其实就是创建一个新的DubboExport对象。
- 服务端只做了一件事,根据新的URL,创建新的DubboExport对象。
具体流程
- CuratorWatc.precess调用到ChildListener.childChanged()
- AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls).url:provider协议URL,urls为empty协议或者override协议地址:configurators节点下的
- 遍历urls
- 判断和url是否匹配,主要是判断目录是否相等
- result = new HashMap<String, List<URL>>().创建新的目录和override的list的映射
- 覆盖notified全局变量,ConcurrentMap<URL, Map<String, List<URL>>>prividerUrl和configurators和override映射
- .saveProperties(url):保存本地文件,把override协议列表保存到本地缓存文件
- 文件properties,key为服务名称,value为空格分割的orverride协议地址
- 文件地址:用户目录+/.dubbo/dubbo-registry-应用名称—服务地址.cache
- 保存文件的目的:服务在启动的时候可以优先从本地加载。AbstractRegistry构造函数中调用
- loadProperties():加载缓存文件中的override配置,不需要zookeeper也可以完成属性覆盖
- notify(url.getBackupUrls()):触发覆盖
- listener.notify(categoryList):调用overrideListener,privider端口,则只修改dubboExport
- 将override的URL集合,专为List<Configurator>
- 获取dubbo协议地址
- URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
- 当前dubboURL
- URL currentUrl = exporter.getInvoker().getUrl();
- 把override协议中的属性合并到dubbo协议中Configurator中
- URL newUrl = getConfigedInvokerUrl(configurators, originUrl);
- 如果当前url和新url不同,则重新创建DubboExport对象,持有inoker执行链,invoker中有新的url
- RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl);
总结:
- verride协议中修改或者添加的属性合并到originUrl中,生成新的URL
- 比较新的URL和当前URL是否相同
- 不相同则重新完成服务暴露:其实就是创建一个新的DubboExport对象。
Override,Configurators在服务端触发
.notify方法,服务端只做了一件事,根据新的URL,创建新的DubboExport对象。
源码解析
1.AbstractRegistry.notify
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
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>();
//创建新的目录和override的list的映射
result.put(category, categoryList);
}
categoryList.add(u);
}
}
if (result.size() == 0) {
return;
}
//获取缓存中的override协议的list
Map<String, List<URL>> categoryNotified = notified.get(url);
if (categoryNotified == null) {
notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
categoryNotified = notified.get(url);
}
for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
String category = entry.getKey();
List<URL> categoryList = entry.getValue();
//覆盖notified缓存中的
categoryNotified.put(category, categoryList);
//把override协议列表保存到本地缓存文件中
saveProperties(url);
//事件触发核心
listener.notify(categoryList);
}
}
2.RegistryProtocol.OverrideListener.Notify
public synchronized void notify(List<URL> urls) {
//根据覆盖override url生成Configurator对象
List<Configurator> configurators = RegistryDirectory.toConfigurators(matchedUrls);
//The origin invoker
URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
String key = getCacheKey(originInvoker);
ExporterChangeableWrapper<?> exporter = bounds.get(key);
if (exporter == null) {
logger.warn(new IllegalStateException("error state, exporter should not be null"));
return;
}
//The current, may have been merged many times
URL currentUrl = exporter.getInvoker().getUrl();
//Merged with this configuration
//把override协议中修改或者添加的属性合并到originUrl中
URL newUrl = getConfigedInvokerUrl(configurators, originUrl);
//如果新旧url不相等
if (!currentUrl.equals(newUrl)) {
//这里URL对象改了,需要重新创建DubboExporter对象,覆盖之前的那个对象
RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl);
logger.info("exported provider url changed, origin url: " + originUrl + ", old export url: " + currentUrl + ", new export url: " + newUrl);
}
}
总结:上面内容中,每个从业务流程和源码角度进行了详细分析,如果大家有疑问或者对文章排版任何方面有建议都可以留言评论,看到都会及时回复大家。
知识总结,分享不易,全文手敲,欢迎大家关注点赞评论收藏。