dubbo解析-不同参数设置方式之间的覆盖关系

本文基于dubbo 2.7.5版本代码

dubbo可以通过多种方式配置,比如系统属性、配置文件、XXXConfig配置对象等。这就带来一个问题,当通过多种方式配置同一个属性时,不同方式之间该属性值的覆盖关系是什么样子的。本文来研究这个问题。

一、dubbo的五种配置方式

dubbo可以配置参数值的方式的有:

  1. 系统属性,通过-D设置,或者环境变量
  2. 配置中心
  3. XXXConfig API编程方式配置,可以通过springboot配置文件、Annotation注解设置XXXConfig的属性值
  4. 本地dubbo.properties配置文件
  5. 对于消费端来说,还有一些配置来自服务端

二、各种方式的优先级

下图中,优先级高的配置方式可以覆盖优先级低的,其中通过系统属性设置的配置可以覆盖其他任何方式的配置,也就是使用java命令启动的时候增加-D参数配置优先级最高:
在这里插入图片描述

三、各种配置方式举例

1、服务端配置

服务端配置指的是使用其他四种配置方式对服务提供者进行参数设置,这些参数会发送到注册中心,消费端从注册中心获取到这些配置数据并设置到本地。设置的参数可能仅仅对消费端起作用,比如服务超时时间。

2、系统属性、环境变量

java在启动的时候,可以使用-D选项设置参数,比如:
java -Ddubbo.application.id=server -Ddubbo.registry.address=zookeeper://localhost:2181 …(省略)
在代码里面可以通过System.getProperty(“属性名”)获取属性值。
dubbo也会使用System.getenv读取环境变量,所以我们也可以通过环境变量设置参数。但是环境变量的优先级要低于系统属性。

3、配置中心

该方式是把参数配置放到配置中心,应用程序读取配置中心。以zk作为配置中心为例,将设置的参数做为节点的数据,dubbo读取到数据后,使用如下代码解析:

Properties properties = new Properties();
properties.load(new StringReader(content));//content便是从配置中心读取的节点数据

配置中心可以参见文章:《dubbo解析-详解配置中心

4、API编程方式

API编程方式是设置XXXConfig对象的属性,比如设置ApplicationConfig、ServiceConfig、ReferenceConfig等对象。
可以通过注解、springboot配置文件、代码编程三种方式设置:
(1)注解:@Service(cache=“lru”,retries=3)
(2)springboot配置文件:

dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

(3)代码编程(来源官网):

public static void main(String[] args) throws IOException {
    ServiceConfig<GreetingsService> service = new ServiceConfig<>();
    service.setApplication(new ApplicationConfig("first-dubbo-provider"));
    service.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
    service.setInterface(GreetingsService.class);
    service.setRef(new GreetingsServiceImpl());
    service.export();
    System.out.println("first-dubbo-provider is running.");
    System.in.read();
}

5、本地dubbo.properties配置文件

在本地创建dubbo.properties文件,该文件的配置方式与springboot配置文件的规则一致。

四、代码解析

下面从消费端启动过程分析覆盖关系是如何实现的。
首先dubbo启动的时候加载配置中心的配置数据,代码如下:

	//DubboBootstrap初始化调用下面的方法
	private void startConfigCenter() {
        Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();
        if (CollectionUtils.isNotEmpty(configCenters)) {
            CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration();
            for (ConfigCenterConfig configCenter : configCenters) {
                //代码删减
				//prepareEnvironment见下方[1]处
				//prepareEnvironment从配置中心获取配置数据,并设置到Environment对象
		    	compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
            }
            environment.setDynamicConfiguration(compositeDynamicConfiguration);
        }
        //更新XXXConfig对象属性值,代码见[2]处
        configManager.refreshAll();
    }
    [1]
    private DynamicConfiguration prepareEnvironment(ConfigCenterConfig configCenter) {
        if (configCenter.isValid()) {
            //代码删减
            DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());
            String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());
            String appGroup = getApplication().getName();
            String appConfigContent = null;
            if (isNotEmpty(appGroup)) {
                appConfigContent = dynamicConfiguration.getProperties
                        (isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
                                appGroup
                        );
            }
            try {
            	//configContent和appConfigContent均是从配置中心获取的数据,获取后设置到Environment对象中
                environment.setConfigCenterFirst(configCenter.isHighestPriority());
                environment.updateExternalConfigurationMap(parseProperties(configContent));
                environment.updateAppExternalConfigurationMap(parseProperties(appConfigContent));
            } catch (IOException e) {
                throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
            }
            return dynamicConfiguration;
        }
        return null;
    }
    [2]
    public void refreshAll() {
        write(() -> {
        	//更新XXXConfig对象,下面的每个refresh方法都会调用[3]处代码
            getApplication().ifPresent(ApplicationConfig::refresh);
            getMonitor().ifPresent(MonitorConfig::refresh);
            getModule().ifPresent(ModuleConfig::refresh);
            getProtocols().forEach(ProtocolConfig::refresh);
            getRegistries().forEach(RegistryConfig::refresh);
            getProviders().forEach(ProviderConfig::refresh);
            getConsumers().forEach(ConsumerConfig::refresh);
        });
    }
	[3]
	public void refresh() {
        Environment env = ApplicationModel.getEnvironment();
        try {
            CompositeConfiguration compositeConfiguration = env.getConfiguration(getPrefix(), getId());//getConfiguration方法代码见[6]处
            Configuration config = new ConfigConfigurationAdapter(this);
            //env.isConfigCenterFirst()默认是true
            if (env.isConfigCenterFirst()) {
            	//下面的config表示使用API方式设置的属性
            	[5]
                compositeConfiguration.addConfiguration(4, config);
            } else {
                compositeConfiguration.addConfiguration(2, config);
            }
            //代码删减,删减的代码功能是调用compositeConfiguration的getXXX方法设置属性
        } catch (Exception e) {
            logger.error("Failed to override ", e);
        }
    }
    [6]
    public CompositeConfiguration getConfiguration(String prefix, String id) {
    	//compositeConfiguration内部有List对象,addConfiguration方法将入参添加到该List对象中
        CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
        compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));//getSystemConfig获取系统属性,该配置方式优先级最高
        compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));//getEnvironmentConfig获取环境变量,方法内部使用System.getenv方法获取指定的属性值
        compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));//getAppExternalConfig获取配置中心的属性
        compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));//getExternalConfig获取配置中心的属性
        compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));//getPropertiesConfig获取本地配置文件中的属性
        return compositeConfiguration;
    }

getConfiguration方法聚合了三种配置方式设置的属性,CompositeConfiguration的List属性中各个元素的下标就表示了配置方式的优先级,下标越小优先级越高,再来看上面代码[6]处,默认情况下执行[6]处代码,也就是说通过API方式设置的属性会放在List属性中下标为4的位置,即优先级比本地配置文件dubbo.properties配置方式高一级。
ServiceConfig和ReferenceConfig对象属性的更新也是调用上面代码[3]处完成的。
上面的代码根据各个配置方式设置的属性完成了对XXXConfig对象的更新,之后服务端和消费端就根据这些XXXConfig对象的属性值去暴露服务和引用远程服务。
还有一点需要注意,ApplicationConfig、ModuleConfig、ConsumerConfig等对象有一些同名的属性,那么这里也存在属性覆盖的问题。消费端各个对象优先级如下:
ApplicationConfig<ModuleConfig<ConsumerConfig<ReferenceConfig<MethodConfig
服务端各个对象优先级关系如下:
ApplicationConfig<ModuleConfig<ProviderConfig<ProtocolConfig<ServiceConfig<MethodConfig
最后来看对于消费端是如何处理服务端配置的,代码如下:

public class ClusterUtils {
	//代码有删减
	//入参remoteUrl是从注册中心获取的服务端参数组成的URL对象,
	//入参localMap是消费端属性配置的汇总
	//mergeUrl方法的返回值是服务端和消费端配置融合后的结果
    public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {
        Map<String, String> map = new HashMap<String, String>();
        Map<String, String> remoteMap = remoteUrl.getParameters();
        if (remoteMap != null && remoteMap.size() > 0) {
            map.putAll(remoteMap);//将服务端参数设置到map中
            map.remove(THREAD_NAME_KEY);
            map.remove(DEFAULT_KEY_PREFIX + THREAD_NAME_KEY);
            map.remove(THREADPOOL_KEY);
            map.remove(DEFAULT_KEY_PREFIX + THREADPOOL_KEY);
            map.remove(CORE_THREADS_KEY);
            map.remove(DEFAULT_KEY_PREFIX + CORE_THREADS_KEY);
            map.remove(THREADS_KEY);
            map.remove(DEFAULT_KEY_PREFIX + THREADS_KEY);
            map.remove(QUEUES_KEY);
            map.remove(DEFAULT_KEY_PREFIX + QUEUES_KEY);
            map.remove(ALIVE_KEY);
            map.remove(DEFAULT_KEY_PREFIX + ALIVE_KEY);
            map.remove(Constants.TRANSPORTER_KEY);
            map.remove(DEFAULT_KEY_PREFIX + Constants.TRANSPORTER_KEY);
        }
        if (localMap != null && localMap.size() > 0) {
            Map<String, String> copyOfLocalMap = new HashMap<>(localMap);
            if(map.containsKey(GROUP_KEY)){
                copyOfLocalMap.remove(GROUP_KEY);
            }
            if(map.containsKey(VERSION_KEY)){
                copyOfLocalMap.remove(VERSION_KEY);
            }
            copyOfLocalMap.remove(RELEASE_KEY);
            copyOfLocalMap.remove(DUBBO_VERSION_KEY);
            copyOfLocalMap.remove(METHODS_KEY);
            copyOfLocalMap.remove(TIMESTAMP_KEY);
            copyOfLocalMap.remove(TAG_KEY);
            //在服务端参数的基础上,使用消费端配置覆盖
            map.putAll(copyOfLocalMap);
            map.put(REMOTE_APPLICATION_KEY, remoteMap.get(APPLICATION_KEY));
            String remoteFilter = remoteMap.get(REFERENCE_FILTER_KEY);
            String localFilter = copyOfLocalMap.get(REFERENCE_FILTER_KEY);
            if (remoteFilter != null && remoteFilter.length() > 0
                    && localFilter != null && localFilter.length() > 0) {
                map.put(REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter);
            }
            String remoteListener = remoteMap.get(INVOKER_LISTENER_KEY);
            String localListener = copyOfLocalMap.get(INVOKER_LISTENER_KEY);
            if (remoteListener != null && remoteListener.length() > 0
                    && localListener != null && localListener.length() > 0) {
                map.put(INVOKER_LISTENER_KEY, remoteListener + "," + localListener);
            }
        }
        return remoteUrl.clearParameters().addParameters(map);
    }
}

本文参考:

http://dubbo.apache.org/zh-cn/docs/user/configuration/configuration-load-process.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值