近期开发项目,将Dubbo的配置全部外部化到动态配置中心。这里配置中心我使用的是Apollo。
@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。
其余配置全部配置在这个里面。
然后遇到一个大坑:
这里项目中定义了一个Service,也就是作为Provider提供了版本为provider.auth.version的服务。
package io.github.slankka.provider; @Service(version = "${provider.auth.version:1.0}") @Slf4j public class AuthApiService implements IAuthApi
项目启动之后,注册到注册中心的版本是1.0。
在dubbo命名空间中定义了provider.auth.version=20000,但是启动还是1.0
Apollo启动模式分为两种,一种是在 BeanDefinition阶段就将配置注入Spring容器内的Environment。另一种是在PostBeanFactory的时候启动。
而Dubbo启动的时候,所有的Service是ServiceBean的实例对应生成的代理类,所有的Reference是ReferenceBean的实例对应生成的代理类。
因此如果这里使用placeholder要被Spring识别,那么将必须选用第一种启动方式。
在本地项目的application.properties中配置:
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
apollo.bootstrap.enabled = true apollo.bootstrap.namespaces = application,dubbo
那么什么时候使用 placeholder什么时候使用 dubbo.service.
答案很显然是,如果多个接口都用共用同一个版本变量进行设置,用Apollo+Spring的方式进行处理。如果每一个接口都配置不同的版本,可以用Dubbo的方式定义。
解决Dubbo 2.7.3版本使用ConfigCenterConfig集成Apollo No Provider found的问题