Dubbo 3.0 : Reference 扫描流程

1,418 阅读3分钟

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 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()));
}

再来看一下那个图 :

image.png

三 . 扫描生成 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 : 这一段比较多 , 后面单独看看



整体流程图

image.png image.png

ReferenceConfig 数据

<dubbo:reference id="demoService" />

image.png

总结

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