近期開發項目,將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
apollo.bootstrap.enabled = true apollo.bootstrap.namespaces = application,dubbo
那麼何時使用 placeholder何時使用 dubbo.service.
答案很顯然是,若是多個接口都用共用同一個版本變量進行設置,用Apollo+Spring的方式進行處理。若是每個接口都配置不一樣的版本,能夠用Dubbo的方式定義。
解決Dubbo 2.7.3版本使用ConfigCenterConfig集成Apollo No Provider found的問題