解決Dubbo 2.7.3版本使用ConfigCenterConfig集成Apollo No Provider found的問題

Dubbo 2.7.3 集成Apollo

問題描述

Dubbo 2.7.3支持配置中心外部化配置, 所以只須要定義一個ConfigCenterConfig的Bean。html

@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;
    }
}

問題:java

  1. Apollo 找不到 meta。
  2. Dubbo 找不到 provider

解決方案

1. Apollo 找不到meta

Apollo的jar 的apollo-core的配置文件明明聲明瞭PRO.meta="apollo.xxxxx.com:8080"。
這個問題出如今git

apollo.bootstrap.enabled = false

若是要堅持這樣配置,須要增長github

apollo.meta=apollo.xxxxx.com:8080

更新:此問題還有更深刻的分析Apollo報錯找不到apollo.meta的問題解決方案spring

2. Dubbo 找不到Provider

仔細看日誌 Interface: com.xxx.xxx.service,若是後面沒有跟着版本號例如: 1.2.0,則說明版本沒有定義。
問題是由於定義了佔位符,而Dubbo啓動的時候,建立ReferenceBean的類是個BeanPostProcessor,啓動比較早,而apollo.bootstrap.enabled=false。
則Dubbo建立這個IRExecutionService對應的Bean類的時候,找不到version,可是他catch吃掉異常了。等於沒有配置version。apache

apollo.bootstrap.enabled = false

@Reference(version = "${job.service.version}", retries = 0, lazy = true)
private IRExecutionService executionService;

則緣由是Dubbo不能從ConfigCenterConfig讀取版本配置,或者太遲了,若是要解決很簡單 ,可是太依賴Apollo提早初始化開關。bootstrap

若是堅持要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");內的。app

最後一個小問題

Dubbo 會默認讀取 dubbo這個 Apollo的namespace,若是用自定義的namespace,他也會讀取,由於不存在而啓動減慢,因此爲了加快啓動速度,建議建立Apollo的 一個空dubbo的namespace。ide

相關文章
相關標籤/搜索