Dubbo 2.7.3 集成Apollo
問題描述
Dubbo 2.7.3支持配置中心外部化配置, 因此只需要定義一個ConfigCenterConfig的Bean。
@EnableDubbo(scanBasePackages = {"com.slankka.cloud.dubbo"})
@Configuration
public class DubboConfig {
@Bean
public ConfigCenterConfig configCenterConfig() {
ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
configCenterConfig.setAddress("apollo.xxxxx.com:8080");
configCenterConfig.setProtocol("apollo");
configCenterConfig.setNamespace("dubbo");
configCenterConfig.setGroup(null);
return configCenterConfig;
}
}
問題:
- Apollo 找不到 meta。
- Dubbo 找不到 provider
解決方案
1. Apollo 找不到meta
Apollo的jar 的apollo-core的配置文件明明聲明了PRO.meta="apollo.xxxxx.com:8080"。
這個問題出現在
apollo.bootstrap.enabled = false
如果要堅持這樣配置,需要增加
apollo.meta=apollo.xxxxx.com:8080
更新:此問題還有更深入的分析: Apollo報錯找不到apollo.meta的問題解決方案
2. Dubbo 找不到Provider
仔細看日志 Interface: com.xxx.xxx.service,如果后面沒有跟着版本號例如: 1.2.0,則說明版本沒有定義。
問題是因為定義了占位符,而Dubbo啟動的時候,創建ReferenceBean的類是個BeanPostProcessor,啟動比較早,而apollo.bootstrap.enabled=false。
則Dubbo創建這個IRExecutionService對應的Bean類的時候,找不到version,但是他catch吃掉異常了。等於沒有配置version。
apollo.bootstrap.enabled = false
@Reference(version = "${job.service.version}", retries = 0, lazy = true)
private IRExecutionService executionService;
則原因是Dubbo不能從ConfigCenterConfig讀取版本配置,或者太遲了,如果要解決很簡單 ,但是太依賴Apollo提前初始化開關。
如果堅持要apollo.bootstrap.enabled = false,強制使用Dubbo自行處理這個變量的解析怎么辦?
package io.github.slankka.dubbo-apollo.server.config;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.Environment;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ConfigCenterConfig;
import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor;
import org.apache.dubbo.configcenter.DynamicConfiguration;
import org.apache.dubbo.configcenter.DynamicConfigurationFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.PropertySource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties;
/**
* Project: dubbo-apollo
*
* @author slankka on 2019/8/29.
*/
@ConditionalOnProperty(name = "apollo.bootstrap.enabled", havingValue = "false", matchIfMissing = true)
@Component(value = ReferenceAnnotationBeanPostProcessor.BEAN_NAME)
public class ReferencedAnnotationPatch extends ReferenceAnnotationBeanPostProcessor {
private ApplicationContext myContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
myContext = applicationContext;
super.setApplicationContext(applicationContext);
}
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName,
Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
eagerInitConfigCenter();
Configuration configuration = Environment.getInstance().getConfiguration();
List<PropertySource<?>> propertySources = new ArrayList<>();
propertySources.add(new PropertySource<Configuration>("dubboConfigCenter", configuration) {
@Override
public Object getProperty(String name) {
return configuration.getProperty(name);
}
});
PropertySourcesPlaceholdersResolver propertySourcesPlaceholdersResolver = new PropertySourcesPlaceholdersResolver(propertySources);
for (String attribute : attributes.keySet()) {
Object stringAttr = attributes.get(attribute);
if (stringAttr instanceof String) {
Object value = propertySourcesPlaceholdersResolver.resolvePlaceholders(attributes.getString(attribute));
attributes.put(attribute, value);
}
}
return super.doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
}
private void eagerInitConfigCenter() {
ConfigCenterConfig configCenter = myContext.getBean(ConfigCenterConfig.class);
if (configCenter.isValid()) {
if (configCenter.checkOrUpdateInited()) {
configCenter.refresh();
URL url = configCenter.toUrl();
DynamicConfigurationFactory factories = ExtensionLoader
.getExtensionLoader(DynamicConfigurationFactory.class)
.getExtension(url.getProtocol());
DynamicConfiguration dynamicConfiguration = factories.getDynamicConfiguration(url);
String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());
ApplicationConfig application = myContext.getBean(ApplicationConfig.class);
String appGroup = application.getName();
String appConfigContent = null;
if (StringUtils.isNotEmpty(appGroup)) {
appConfigContent = dynamicConfiguration.getProperties
(StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
appGroup
);
}
try {
Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority());
Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent));
Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent));
} catch (IOException e) {
throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
}
}
}
}
}
則能糾正Dubbo 的ReferenceAnnotationBeanPostProcessor 行為,因為這個時候,已經有ConfigCenterConfig這個Bean了,所以讓ConfigCenter提前啟動,從而使得@Reference注解的占位符能夠被解析。
注意,這個占位符是配置在Namespace("dubbo");內的。
最后一個小問題
Dubbo 會默認讀取 dubbo這個 Apollo的namespace,如果用自定義的namespace,他也會讀取,因為不存在而啟動減慢,所以為了加快啟動速度,建議創建Apollo的 一個空dubbo的namespace。