Dubbo 30 Reference 扫描流程

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 juejin.cn/post/694164…

Github : 👉 github.com/black-ant

CASE 备份 : 👉 gitee.com/antblack/ca…

一 .前言

前面几篇看了 Dobbo 的注册流程 , 这一阶段看看Dubbo 的发现流程

Dubbo 的发现包括以下几个环节 :

  • Reference 的扫描和代理
  • Reference 服务发现
  • 负载均衡
  • 服务降级

二 . Reference 获取

访问入口

1
2
3
4
5
6
7
8
9
10
11
java复制代码public DubboBootstrap start() {

//..省略注册和容器初始化

// Reference 扫描的起点为 DubboBootstrap , 其中有这样一段代码
// 发起 Refer Services 处理
referServices();

//.........

}

2.1 初始化逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
java复制代码private void referServices() {
if (cache == null) {
cache = ReferenceConfigCache.getCache();
}
// <dubbo:reference sticky="false" id="demoService" /> -> 2.2 列表获取
configManager.getReferences().forEach(rc -> {
// TODO, compatible with ReferenceConfig.refer()
ReferenceConfig<?> referenceConfig = (ReferenceConfig<?>) rc;
referenceConfig.setBootstrap(this);
if (!referenceConfig.isRefreshed()) {
referenceConfig.refresh();
}

if (rc.shouldInit()) {
if (rc.shouldReferAsync()) {
ExecutorService executor = executorRepository.getExportReferExecutor();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
cache.get(rc);
} catch (Throwable t) {
logger.error("refer async catch error : " + t.getMessage(), t);
}
}, executor);

asyncReferringFutures.add(future);
} else {
cache.get(rc);
}
}
});
}

2.2 References 管理

列表获取

1
2
3
4
5
6
java复制代码// 2.1 里面可以看到从缓存中获取 , 以下看一下获取的流程
configManager.getReferences()

public Collection<ReferenceConfigBase<?>> getReferences() {
return getConfigs(getTagName(ReferenceConfigBase.class));
}

看到这里基本上就能回忆起来了 , 上文这个地方就已经见过了, 其主要原理是获取 Config 前缀 ,然后从一个集合中获取对应的配置信息 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码public static String getTagName(Class<?> cls) {
String tag = cls.getSimpleName();
for (String suffix : SUFFIXES) {
if (tag.endsWith(suffix)) {
tag = tag.substring(0, tag.length() - suffix.length());
break;
}
}
return StringUtils.camelToSplitName(tag, "-");
}


final Map<String, Map<String, AbstractConfig>> configsCache = newMap();
// configType -> reference
private <C extends AbstractConfig> Map<String, C> getConfigsMap(String configType) {
return (Map<String, C>) read(() -> configsCache.getOrDefault(configType, emptyMap()));
}

再来看一下那个图 :

image.png

三 . 扫描生成 Reference

3.1 扫描的入口

接下来看看Reference 是怎么扫描生成的 , 和上文看到的一样 , 一切的起点是 InitializingBean # afterPropertiesSet 触发的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码C- ReferenceBean
public void afterPropertiesSet() throws Exception {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();

// 获取当前 Bean 的 BeanDefinition
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId());
this.interfaceClass = (Class<?>) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_CLASS);
this.interfaceName = (String) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_NAME);

// 获取 Reference 属性
if (beanDefinition.hasAttribute(Constants.REFERENCE_PROPS)) {
referenceProps = (Map<String, Object>) beanDefinition.getAttribute(Constants.REFERENCE_PROPS);
} else {
// 省略其他图解的属性解析
}

// 获取 ReferenceBeanManager 并且发起注册逻辑
ReferenceBeanManager referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
referenceBeanManager.addReference(this);
}

3.2 RefenceBean 管理入口

下面来看一下 Reference 的只要流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码public void addReference(ReferenceBean referenceBean) throws Exception {

String referenceBeanName = referenceBean.getId();
PropertyResolver propertyResolver = applicationContext.getEnvironment();

// 校验缓存中是否存在 ,且是否为同一对象
String referenceKey = ReferenceBeanSupport.generateReferenceKey(referenceBean, propertyResolver);
ReferenceBean oldReferenceBean = referenceIdMap.get(referenceBeanName);

// 省略oldRefence 校验逻辑 , 该逻辑要求如果缓存中存在 , 则其应用地址应该一致
// PS : 因为使用的的 != 比较的地址 , 避免重新重复的 Reference 对象

referenceIdMap.put(referenceBeanName, referenceBean);
// 保存缓存,将引用键映射到referencebename
this.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);

// 如果在 prepareReferenceBeans()之后添加引用,应该立即初始化它
if (initialized) {
initReferenceBean(referenceBean);
}
}

3.3 RefenceBean 初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
java复制代码private synchronized void  initReferenceBean(ReferenceBean referenceBean) throws Exception {

if (referenceBean.getReferenceConfig() != null) {
return;
}

// 首先构建 referenceKey
String referenceKey = ReferenceBeanSupport.generateReferenceKey(referenceBean, applicationContext.getEnvironment());

// 从缓存中获取 ReferenceConfig
// PS : 看过 Dubbo 层级图可以看到 , 这是第二层 Config 层最核心的2个类之一
ReferenceConfig referenceConfig = referenceConfigMap.get(referenceKey);
if (referenceConfig == null) {

//创建一个真实的 ReferenceConfig
Map<String, Object> referenceAttributes = ReferenceBeanSupport.getReferenceAttributes(referenceBean);
referenceConfig = ReferenceCreator.create(referenceAttributes, applicationContext)
.defaultInterfaceClass(referenceBean.getObjectType())
.build();

// set id if it is not a generated name
if (referenceBean.getId() != null && !referenceBean.getId().contains("#")) {
referenceConfig.setId(referenceBean.getId());
}

// Step 1 : 缓存 : cache referenceConfig
referenceConfigMap.put(referenceKey, referenceConfig);

// Step 2 : 注册 : register ReferenceConfig
// 核心 : 调用逻辑添加 Config 到集合中
DubboBootstrap.getInstance().reference(referenceConfig);
}

// Step 3 : 关联 associate referenceConfig to referenceBean
referenceBean.setKeyAndReferenceConfig(referenceKey, referenceConfig);
}

3.4 添加到集合中

1
2
3
4
5
6
java复制代码// C- DubboBootstrap
public DubboBootstrap reference(ReferenceConfig<?> referenceConfig) {
referenceConfig.setBootstrap(this);
configManager.addReference(referenceConfig);
return this;
}

核心添加流程

1
2
3
4
5
6
7
JAVA复制代码// PS : 中秋耶看过这个逻辑 , 获取集合并且添加
protected <T extends AbstractConfig> T addConfig(AbstractConfig config, boolean unique) {
return (T) write(() -> {
Map<String, AbstractConfig> configsMap = configsCache.computeIfAbsent(getTagName(config.getClass()), type -> newMap());
return addIfAbsent(config, configsMap, unique);
});
}

3.5 ReferenceBeanManager 补充

ReferenceBeanManager 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码// 注意 , 这里实现了 ApplicationContextAware , 以完成容器的通知操作
public class ReferenceBeanManager implements ApplicationContextAware {
//reference bean id/name -> ReferenceBean
private Map<String, ReferenceBean> referenceIdMap = new ConcurrentHashMap<>();

//reference key -> [ reference bean names ]
private Map<String, List<String>> referenceKeyMap = new ConcurrentHashMap<>();

//reference key -> ReferenceConfig instance
private Map<String, ReferenceConfig> referenceConfigMap = new ConcurrentHashMap<>();

private ApplicationContext applicationContext;

}

补充

3.3 创建 ReferenceConfig 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
java复制代码
referenceConfig = ReferenceCreator.create(referenceAttributes, applicationContext)
.defaultInterfaceClass(referenceBean.getObjectType())
.build();

// Step 1 : 准备 ReferenceCreator 对象
public static ReferenceCreator create(Map<String, Object> attributes, ApplicationContext applicationContext) {
return new ReferenceCreator(attributes, applicationContext);
}

// Step 2 : 创建 ReferenceConfig
public final ReferenceConfig build() throws Exception {
ReferenceConfig configBean = new ReferenceConfig();
// 核心配置
configureBean(configBean);
return configBean;

}

// Step 3 : 核心配置流程
protected void configureBean(ReferenceConfig configBean) throws Exception {
populateBean(attributes, configBean);
configureMonitorConfig(configBean);
configureApplicationConfig(configBean);
configureModuleConfig(configBean);
configureConsumerConfig(attributes, configBean);
}

// PS : 这一段比较多 , 后面单独看看

整体流程图

image.png
image.png

ReferenceConfig 数据

1
java复制代码<dubbo:reference id="demoService" />

image.png

总结

Reference 比较有看头的就是 Spring 的深入使用 , 这个点值得深入学习一下

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%