Dubbo配置彻底外部化实践,使用动态配置中心的注意事项

问题描述

近期开发项目,将Dubbo的配置所有外部化到动态配置中心。这里配置中心我使用的是Apollo。html

@Configuration
public class DubboConfig {
  @Bean
  public ConfigCenterConfig configCenterConfig() {
    ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
    configCenterConfig.setAddress("apollo.xxxxx.com");
    configCenterConfig.setProtocol("apollo");
    configCenterConfig.setNamespace("dubbo");
    configCenterConfig.setGroup(null);
    return configCenterConfig;
  }
}

这里使用dubbo这个namespace。git

其他配置所有配置在这个里面。github

而后遇到一个大坑:apache

这里项目中定义了一个Service,也就是做为Provider提供了版本为provider.auth.version的服务。bootstrap

package io.github.slankka.provider;

@Service(version = "${provider.auth.version:1.0}")
@Slf4j
public class AuthApiService implements IAuthApi

项目启动以后,注册到注册中心的版本是1.0。app

在dubbo命名空间中定义了provider.auth.version=20000,可是启动仍是1.0ide

问题分析

Apollo启动模式分为两种,一种是在 BeanDefinition阶段就将配置注入Spring容器内的Environment。另外一种是在PostBeanFactory的时候启动。
而Dubbo启动的时候,全部的Service是ServiceBean的实例对应生成的代理类,全部的Reference是ReferenceBean的实例对应生成的代理类。
所以若是这里使用placeholder要被Spring识别,那么将必须选用第一种启动方式。this

问题解决

在本地项目的application.properties中配置:spa

apollo.bootstrap.enabled = true
apollo.bootstrap.namespaces = application,dubbo

注意,这里apollo.bootstrap.namespaces 中加入了dubbo,这里有一个疑问:
以前配置Dubbo的时候不是定义了ConfigCenterConfig的namespace了吗,为何还要定义一次。代理

configCenterConfig.setNamespace("dubbo");

缘由是:
Dubbo的ServiceBean在被Spring工厂构建出来的时候,就须要这个变量。但若是没有配置dubbo到apollo.bootstrap.namespaces,Spring会报错。
若是接口上写了默认版本,上例是1.0,则Spring不会报错,可是构建出来的对应ServiceBean中的版本将会是1.0,而Dubbo启动以后依然不会修改这个版本。

新的问题

为何Dubbo的namespace中配置了这个变量,并且Dubbo在启动早起阶段就已经拉到了这些变量,但版本仍旧没有发生改变?

看一下Dubbo代码:

//package org.apache.dubbo.common.config;

public abstract class AbstractPrefixConfiguration implements Configuration {
    protected String id;
    protected String prefix;

    public AbstractPrefixConfiguration(String prefix, String id) {
        if (StringUtils.isNotEmpty(prefix) && !prefix.endsWith(".")) {
            this.prefix = prefix + ".";
        } else {
            this.prefix = prefix;
        }
        this.id = id;
    }

    @Override
    public Object getProperty(String key, Object defaultValue) {
        Object value = null;
        if (StringUtils.isNotEmpty(prefix)) {
            if (StringUtils.isNotEmpty(id)) {
                value = getInternalProperty(prefix + id + "." + key);
            }
            if (value == null) {
                value = getInternalProperty(prefix + key);
            }
        } else {
            value = getInternalProperty(key);
        }
        return value != null ? value : defaultValue;
    }
}

已知Dubbo是经过Configuration 的getProperty获取version等等这些属性,
这里能够看到用prefix + key的方式做为 property的名字来获取变量的值。
通过Debug发现,Dubbo的 AbstractPrefixConfiguration类的prefix正好是接口的FQCN,在本例中为:

AbstractPrefixConfiguration.prefix=dubbo.service.io.github.slankka.provider.IAuthApi.

那么key=version
所以能够推断出,Dubbo的外部化配置指定的Namespace中,若是要指定版本,且但愿通过Dubbo的Environment处理,那么必定要用这种形式:

dubbo.service.io.github.slankka.provider.IAuthApi.version=20000

总结

  1. 若是使用placeholder的方式定义Service版本,那么根据习惯,要确保这些变量放在Spring启动阶段就能读到的地方。
  2. 若是要在Dubbo的namespace中定义,被Dubbo处理,那么要符合Dubbo的命令规则。dubbo.service.xxxxxxx.version等等这种形式。
  3. 若是使用placeholder的方式定义,但但愿被Apollo直接处理,那么须要配置:
apollo.bootstrap.enabled = true
apollo.bootstrap.namespaces = application,dubbo

那么何时使用 placeholder何时使用 dubbo.service. .version方式定义呢?

答案很显然是,若是多个接口都用共用同一个版本变量进行设置,用Apollo+Spring的方式进行处理。若是每个接口都配置不一样的版本,能够用Dubbo的方式定义。

进一步了解Dubbo和Apollo集成的遇到的有趣问题

解决Dubbo 2.7.3版本使用ConfigCenterConfig集成Apollo No Provider found的问题

相关文章
相关标签/搜索