首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
一 .前言
前面几篇看了 Dobbo 的注册流程 , 这一阶段看看Dubbo 的发现流程
Dubbo 的发现包括以下几个环节 :
- Reference 的扫描和代理
- Reference 服务发现
- 负载均衡
- 服务降级
二 . Reference 获取
访问入口
public DubboBootstrap start() {
//..省略注册和容器初始化
// Reference 扫描的起点为 DubboBootstrap , 其中有这样一段代码
// 发起 Refer Services 处理
referServices();
//.........
}
2.1 初始化逻辑
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 管理
列表获取
// 2.1 里面可以看到从缓存中获取 , 以下看一下获取的流程
configManager.getReferences()
public Collection<ReferenceConfigBase<?>> getReferences() {
return getConfigs(getTagName(ReferenceConfigBase.class));
}
看到这里基本上就能回忆起来了 , 上文这个地方就已经见过了, 其主要原理是获取 Config 前缀 ,然后从一个集合中获取对应的配置信息 :
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()));
}
再来看一下那个图 :
三 . 扫描生成 Reference
3.1 扫描的入口
接下来看看Reference 是怎么扫描生成的 , 和上文看到的一样 , 一切的起点是 InitializingBean # afterPropertiesSet 触发的
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 的只要流程
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 初始化
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 添加到集合中
// C- DubboBootstrap
public DubboBootstrap reference(ReferenceConfig<?> referenceConfig) {
referenceConfig.setBootstrap(this);
configManager.addReference(referenceConfig);
return this;
}
核心添加流程
// 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 属性
// 注意 , 这里实现了 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 流程
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 : 这一段比较多 , 后面单独看看
整体流程图
ReferenceConfig 数据
<dubbo:reference id="demoService" />
总结
Reference 比较有看头的就是 Spring 的深入使用 , 这个点值得深入学习一下