Dubbo基础(二)- Provider的初始化过程A:配置读取过程refresh过程

在上一篇文章中,主要用了 dubbo-samples的一个简单例子,来跑通了Dubbo例子,那么这一篇文章中,将以上一篇文章为基础,研究下Dubbo Provider是如何跑起来的。

本文以以下几个问题展开:

  1. Dubbo 是 以什么为单位 对外提供服务的?
  2. Dubbo 初始化各个Config的配置是怎么样的过程呢?
  3. Dubbo 在获取配置过程中,有什么优先级?

以上问题看完文章后相信大家就可以清楚,若有疑问,关注博主公众号:六点A君,回复标题获取最新答案><

ServiceConfig

以下思路是先从设置字段变量,然后再关注export方法入手:

变量设置

先把代码找过来:

        ServiceConfig<HelloService> service = new ServiceConfig<>();
        service.setApplication(new ApplicationConfig("dubbo-provider"));
        service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        service.setInterface(HelloService.class);
        service.setRef(new HelloServiceImpl());
        service.export();

代码主要围绕ServiceConfig 展开,上面代码就是设置一些ServiceConfig的属性,例如 ApplicationConfigRegisterConfigInterface 以及ref
在这里插入图片描述
整个ServiceConfig,除了顶层的AbstractConfig是实现Serializable接口外,其他都是一个继承体系,也就是说,ServiceConfig拥有上面4个类的所有字段信息(当然需要时可见的字段信息)。

翻阅代码,发现 ApplicationConfigRegisterConfig 都是在 AbstractInterfaceConfig中,但是他们却又不同,Application只能有一个,而RegisterConfig则是用List存储的,所以也验证了Dubbo注册中心可以有多个。

	// 设置 application
    public void setApplication(ApplicationConfig application) {
        ConfigManager.getInstance().setApplication(application);
        this.application = application;
    }

// 设置 RegisterConfig
    public void setRegistry(RegistryConfig registry) {
        List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
        registries.add(registry);
        setRegistries(registries);
    }

Interfaceref 则是在 ServiceConfig 中,其中, Interface只能为接口,否则会报错,而ref则是对应ServiceConfig的泛型变量。

export

从名字来看,就是暴露服务,那么是一个怎么样的暴露过程呢?
整个export是一个 synchronized 方法,但是这是否会影响效率呢?不过这个方法调用频率并不高,另一方面Java8以后虚拟机对 synchronized 关键字做了充分优化,这可能就是 Dubbo 选择使用 方法级别原因?
里面有两个要做的:

  1. 检查并填充配置
  2. 暴露服务

检查配置是在 checkAndUpdateSubConfigs 中完成的
即检查并且更新一些配置。

checkAndUpdateSubConfigs

checkAndUpdateSubConfigs 中,是一大段代码,里面包括对ServiceConfig中其他字段初始化,以及从注册中心中获取一些配置等,
本届主要细说 completeCompoundConfigs 以及 startConfigCenter

completeCompoundConfigs

这个方法是放在第一,主要是绑定一些指定的配置,但是在ServiceConfig并没有同步的配置,例如 application, module, registries, monitor, protocols, configCenter等,但是里面有优先级,在 registries, monitor 的设置上具有优先级,
ProviderConfig > ModuleConfig > ApplicationConfig
而这样的优先级方法,则是通
== null 实现,即如果已经被设定了,就不会再次设定了。

startConfigCenter

startConfigCenter里面,首先尝试设定 当前 AbstractInterfaceConfig子类的值,即如果AbstractInterfaceConfigconfigCenter不为空,就设置当前子类的 configCenter 值, 在本例中就是 ServiceConfig的
然后,如果 ConfigCenter 不为空,那么就尝试 refresh 一下,从而从新获取配置。
以及 执行 prepareEnviroment 方法

最后,将会由配置管理器 ConfigManager 执行 refreshAll 方法,而这个方法 将 ApplicationConfig, MonitorConfig, ModuleConfig, ProtocolConfig, RegistryConfig, ProviderConfig, ConsumerConfig 会都执行 refresh 方法。

这里只执行 7 个 config的refresh方法,而不是执行所有的 refresh方法

因为 configCenter 的初始化被分开了,并且当 configCenter != null 时候,除了 执行 this.configCenter.refresh(); 还会执行 prepareEnvironment, 而 prepareEnviroment则是进行一些前期环境检查工作。而对于 可配置的 多类型注册中心,则是在这里面进行设定的。

ConfigManager中,

ConfigManager

ConfigManager里面,主要 负责维护以下几个config

    private ApplicationConfig application;
    private MonitorConfig monitor;
    private ModuleConfig module;
    private ConfigCenterConfig configCenter;

    private Map<String, ProtocolConfig> protocols = new ConcurrentHashMap<>();
    private Map<String, RegistryConfig> registries = new ConcurrentHashMap<>();
    private Map<String, ProviderConfig> providers = new ConcurrentHashMap<>();
    private Map<String, ConsumerConfig> consumers = new ConcurrentHashMap<>();

从上面定义可知, 一个应用可以有多个 ProtocolConfig, RegistryConfig, ProviderConfig, ConsumerConfig 等配置,而其他的只能有一个。
并且 ConfigManager属于饿汉式的单例模式,有兴趣可以看: 单例模式之饿汉模式(立即加载)

refresh

refresh方法主要是获取当前config下最新配置,将系统配置,或者外部化配置设置到config中。

    public void refresh() {
        try { 
        // 获取 多个混合配置
            CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
            InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId());
            // 将当前类的 信息放入到配置中
            config.addProperties(getMetaData());
            // 看 isConfig的顺序,从而加载不同配置
            if (Environment.getInstance().isConfigCenterFirst()) {
                // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(3, config);
            } else {
                // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(1, config);
            }

            // 循环,填充参数
            Method[] methods = getClass().getMethods();
            for (Method method : methods) {
                if (MethodUtils.isSetter(method)) {
                    try {
                        String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                        // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
                        if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                            method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                        }
                    } catch (NoSuchMethodException e) {
                        logger.info("Failed to override the property " + method.getName() + " in " +
                                this.getClass().getSimpleName() +
                                ", please make sure every property has getter/setter method provided.");
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Failed to override ", e);
        }
    }

在上一段代码中,首先会 Environment.getInstance().getConfiguration(getPrefix(), getId()); 会去获取 4个渠道里面所有的关于 getPrefix, getId 的配置,如下在 Environment.getConfiguration中代码:

    public CompositeConfiguration getConfiguration(String prefix, String id) {
        CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
        // Config center has the highest priority
        compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
        compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));
        compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));
        compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
		// 最后封装返回复合配置
        return compositeConfiguration;
    }

为什么要将四个渠道配置都获取下来呢?
主要有以下两个原因:

  1. 获取所有的配置,尽量保证如果写了配置,那么就需要填充上
  2. 形成 有序链,从而有利于后面的优先级获取

而在后一端代码:

InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId());
            config.addProperties(getMetaData());

则是为了将metaData数据加入,而在 getMetaData 中,则 使用反射,将 遍历所有的 get*的获取基本类型方法,从而获取已有的配置里面的所有的字段值。
另一方面,如果有 getParameters,也同样回获取 getParameters的键值对,将其一并返回,这样依赖在
另一方面最后以Map返回。

配置优先级

配置优先级关键就是在以下代码:

            if (Environment.getInstance().isConfigCenterFirst()) {
                // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(3, config);
            } else {
                // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(1, config);
            }

由于上一小节中,了解到 compositeConfiguration 中,会包括所有地方的配置,主要是4个地方:
SystemConfiguration ,AppExternalConfiguration , ExternalConfiguration , PropertiesConfiguration
而刚刚从本类中的配置获取到的 InmemoryConfiguration,则是由配置,决定 它的优先级,默认下是 配置中心优先级要高于 外部化配置

设置变量

最后,如何来设定变量呢?
即拿到了配置,就到了该要设定配置的时候了,Dubbo给了一个循环,将 set* 方法拿到,再有上一步获取的配置去配置值。

            for (Method method : methods) {
                if (MethodUtils.isSetter(method)) {
                    try {
                    // 获取值,
                        String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                        // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
                        if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                            method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                        }
                    } catch (NoSuchMethodException e) {
                        logger.info("Failed to override the property " + method.getName() + " in " +
                                this.getClass().getSimpleName() +
                                ", please make sure every property has getter/setter method provided.");
                    }
                }
            }

这一段代码中,主要通过 compositeConfiguration.getString(extractPropertyName(getClass(), method)) 获取配置,而里面则是遍历 compositeConfiguration 的 配置list,而由于优先级先后,所以就有了上一步的优先级定义。

当以配置中心优先时候,当前配置字段生效顺序是:
SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
而当不是配置中心优先时候,配置生效顺序则为:
SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration

而 AbstractConfig,则是程序员自己在程序中,或者是在yml或者xml等配置文件中配置。

为了避免篇幅太大,导致阅读疲劳,下一篇则继续分析 checkAndUpdateSubConfigs 其他部分,以及结合 ConfigCenter 示例 看Dubbo具体如何操作配置优先级的。

关注博主公众号: 六点A君。
哈哈哈,Dubbo小吃街不迷路:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值