Dubbo如何加载配置

doMore 72 2025-04-06

前言

在实际开发过程中,使用 SpringBoot 结合 Dubbo ,在项目启动的时候总是会出现一个, zookeeper net connected 的错误,最后发现是在 org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient 构造方法中会去连接 zk,并且是同步阻塞形式的(代码如下)。时间设置的过短就会报错。

    public CuratorZookeeperClient(URL url) {
        super(url);
        try {
            // ......
            client.getConnectionStateListenable().addListener(new CuratorConnectionStateListener(url));
            client.start();
            // 这里是一个同步阻塞等待,假如超过了 timeout 的时间,当前ZooKeeper客户端还是没有变成“已连接”状态,当前线程就会被唤醒,继续向下执行
            boolean connected = client.blockUntilConnected(timeout, TimeUnit.MILLISECONDS);
            // 判断当前客户端不是“已连接”状态,主动抛出异常
            if (!connected) {
                throw new IllegalStateException("zookeeper not connected");
            }
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

本身我们项目中设置了 dubbo.consumer.timeout 但是这里的时间使用的却不是这个,而是 dubbo.registry.timeout 。由此引发了对该问题的探索,想要知道 dubbo 具体是怎么将配置文件加载的。

详解

pom dependency

在使用 dubbo-spring-boot-autoconfigure 时,会引入额外的 jar 包,以确保 自动配置能够正常识别使用。下面列出主要的:

<dependency>
  <groupId>org.apache.dubbo</groupId>
  <artifactId>dubbo-spring-boot-autoconfigure</artifactId>
  <version>2.7.8</version>
</dependency>
<!-- 自动额外引入的主要的 jar -->
<dependency>
  <groupId>org.apache.dubbo</groupId>
  <artifactId>dubbo-spring-boot-autoconfigure-compatible</artifactId>
  <version>2.7.8</version>
</dependency>
<dependency>
  <groupId>org.apache.dubbo</groupId>
  <artifactId>dubbo</artifactId>
</dependency>
    <dependency>
  <groupId>com.alibaba.spring</groupId>
  <artifactId>spring-context-support</artifactId>
</dependency>

EnableDubboConfig

在 org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration 类上使用了 org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig 注解,该注解通过 @Import 引入了 org.apache.dubbo.config.spring.context.annotation.DubboConfigConfigurationRegistrar 类。由此开启了关于配置的一系列加载。

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));

        boolean multiple = attributes.getBoolean("multiple");

        // Single Config Bindings
        registerBeans(registry, DubboConfigConfiguration.Single.class);

        if (multiple) { 
            // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }

        // Since 2.7.6
        registerCommonBeans(registry);
    }
}

@EnableConfigurationBeanBindings({
        @EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
})
public static class Multiple {
}


/**
 * 此处会把  com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBindings  注解上的 @Import 引入类 com.alibaba.spring.beans.factory.annotation.ConfigurationBeanBindingsRegister 识别
 */
    public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {
        if (!ObjectUtils.isEmpty(annotatedClasses)) {
            Set<Class<?>> classesToRegister = new LinkedHashSet(Arrays.asList(annotatedClasses));
            Iterator<Class<?>> iterator = classesToRegister.iterator();

            while(iterator.hasNext()) {
                Class<?> annotatedClass = (Class)iterator.next();
                if (isPresentBean(registry, annotatedClass)) {
                    iterator.remove();
                }
            }

            AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
            if (logger.isDebugEnabled()) {
                logger.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + Arrays.asList(annotatedClasses) + " .");
            }

            reader.register((Class[])classesToRegister.toArray(com.alibaba.spring.util.ClassUtils.EMPTY_CLASS_ARRAY));
        }
    }


/**
 * 需要注意 上述 是 Bindings 现在是 Binding .
 */
public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    private ConfigurableEnvironment environment;

    public ConfigurationBeanBindingsRegister() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 识别出 EnableConfigurationBeanBindings 中的数组
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName()));
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
        // 实例化 ConfigurationBeanBindingRegistrar 去讲上面识别到的数组,一个一个注册
        ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar();
        registrar.setEnvironment(this.environment);

        for(AnnotationAttributes element : annotationAttributes) {
            // 根据注解中的值 进行初始化,并且讲配置文件或者配置中心的值赋值
            registrar.registerConfigurationBeanDefinitions(element, registry);
        }

    }

    public void setEnvironment(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        this.environment = (ConfigurableEnvironment)environment;
    }
}

ConfigurationBeanBindingRegistrar

public class ConfigurationBeanBindingRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    static final Class ENABLE_CONFIGURATION_BINDING_CLASS = EnableConfigurationBeanBinding.class;
    private static final String ENABLE_CONFIGURATION_BINDING_CLASS_NAME;
    private final Log log = LogFactory.getLog(this.getClass());
    private ConfigurableEnvironment environment;

    public ConfigurationBeanBindingRegistrar() {
    }

    // ......

    // 1. 调用的是 这个方法
    public void registerConfigurationBeanDefinitions(Map<String, Object> attributes, BeanDefinitionRegistry registry) {
        String prefix = (String)AnnotationUtils.getRequiredAttribute(attributes, "prefix");
        prefix = this.environment.resolvePlaceholders(prefix);
        Class<?> configClass = (Class)AnnotationUtils.getRequiredAttribute(attributes, "type");
        boolean multiple = (Boolean)AnnotationUtils.getAttribute(attributes, "multiple", false);
        boolean ignoreUnknownFields = (Boolean)AnnotationUtils.getAttribute(attributes, "ignoreUnknownFields", true);
        boolean ignoreInvalidFields = (Boolean)AnnotationUtils.getAttribute(attributes, "ignoreInvalidFields", true);
        this.registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry);
    }

    private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple, boolean ignoreUnknownFields, boolean ignoreInvalidFields, BeanDefinitionRegistry registry) {
        Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(this.environment.getPropertySources(), this.environment, prefix);
        if (CollectionUtils.isEmpty(configurationProperties)) {
            if (this.log.isDebugEnabled()) {
                this.log.debug("There is no property for binding to configuration class [" + configClass.getName() + "] within prefix [" + prefix + "]");
            }

        } else {
            for(String beanName : multiple ? this.resolveMultipleBeanNames(configurationProperties) : Collections.singleton(this.resolveSingleBeanName(configurationProperties, configClass, registry))) {
                // 2. 进而走到这里
                this.registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, configurationProperties, registry);
            }
            // 5. 注册 com.alibaba.spring.beans.factory.annotation.ConfigurationBeanBindingPostProcessor
            this.registerConfigurationBindingBeanPostProcessor(registry);
        }
    }

    private void registerConfigurationBean(String beanName, Class<?> configClass, boolean multiple, boolean ignoreUnknownFields, boolean ignoreInvalidFields, Map<String, Object> configurationProperties, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(configClass);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        // 3. 关键点 讲 source 设置为 EnableConfigurationBeanBinding
        this.setSource(beanDefinition);
        // 注意: 环境中的配置属性 在此处会被绑定到 attributes (Map)的 configurationProperties 中。
        Map<String, Object> subProperties = this.resolveSubProperties(multiple, beanName, configurationProperties);
        ConfigurationBeanBindingPostProcessor.initBeanMetadataAttributes(beanDefinition, subProperties, ignoreUnknownFields, ignoreInvalidFields);
        registry.registerBeanDefinition(beanName, beanDefinition);
        if (this.log.isInfoEnabled()) {
            this.log.info("The configuration bean definition [name : " + beanName + ", content : " + beanDefinition + "] has been registered.");
        }

    }

    private Map<String, Object> resolveSubProperties(boolean multiple, String beanName, Map<String, Object> configurationProperties) {
        if (!multiple) {
            return configurationProperties;
        } else {
            MutablePropertySources propertySources = new MutablePropertySources();
            propertySources.addLast(new MapPropertySource("_", configurationProperties));
            return PropertySourcesUtils.getSubProperties(propertySources, this.environment, PropertySourcesUtils.normalizePrefix(beanName));
        }
    }

    // 4. 将传进来的 beanDefinition 的source 设置为 com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBinding 注解,source 在 com.alibaba.spring.beans.factory.annotation.ConfigurationBeanBindingPostProcessor 会使用到
    private void setSource(AbstractBeanDefinition beanDefinition) {
        beanDefinition.setSource(ENABLE_CONFIGURATION_BINDING_CLASS);
    }

    private void registerConfigurationBindingBeanPostProcessor(BeanDefinitionRegistry registry) {
        BeanRegistrar.registerInfrastructureBean(registry, "configurationBeanBindingPostProcessor", ConfigurationBeanBindingPostProcessor.class);
    }

    // ......

    private Set<String> resolveMultipleBeanNames(Map<String, Object> properties) {
        Set<String> beanNames = new LinkedHashSet();

        for(String propertyName : properties.keySet()) {
            int index = propertyName.indexOf(".");
            if (index > 0) {
                String beanName = propertyName.substring(0, index);
                beanNames.add(beanName);
            }
        }

        return beanNames;
    }

    private String resolveSingleBeanName(Map<String, Object> properties, Class<?> configClass, BeanDefinitionRegistry registry) {
        String beanName = (String)properties.get("id");
        if (!StringUtils.hasText(beanName)) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(configClass);
            beanName = BeanDefinitionReaderUtils.generateBeanName(builder.getRawBeanDefinition(), registry);
        }

        return beanName;
    }

    static {
        ENABLE_CONFIGURATION_BINDING_CLASS_NAME = ENABLE_CONFIGURATION_BINDING_CLASS.getName();
    }
}

ConfigurationBeanBindingPostProcessor

该类是实现了 org.springframework.beans.factory.config.BeanPostProcessor ,所以会在实例前触发方法,postProcessBeforeInitialization 。

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        BeanDefinition beanDefinition = this.getNullableBeanDefinition(beanName);
        // 这里就是看 beanDefinition 对象中的 source 属性是不是之前的 EnableConfigurationBeanBinding,是的话就直接进行属性绑定以及加载。
        if (this.isConfigurationBean(bean, beanDefinition)) {
            this.bindConfigurationBean(bean, beanDefinition);
            this.customize(beanName, bean);
        }

        return bean;
    }

    private boolean isConfigurationBean(Object bean, BeanDefinition beanDefinition) {
        return beanDefinition != null && ConfigurationBeanBindingRegistrar.ENABLE_CONFIGURATION_BINDING_CLASS.equals(beanDefinition.getSource()) && ObjectUtils.nullSafeEquals(this.getBeanClassName(bean), beanDefinition.getBeanClassName());
    }

    private void bindConfigurationBean(Object configurationBean, BeanDefinition beanDefinition) {
        Map<String, Object> configurationProperties = getConfigurationProperties(beanDefinition);
        boolean ignoreUnknownFields = getIgnoreUnknownFields(beanDefinition);
        boolean ignoreInvalidFields = getIgnoreInvalidFields(beanDefinition);
        // getConfigurationBeanBinder() 的结果为 org.apache.dubbo.spring.boot.autoconfigure.BinderDubboConfigBinder 实例
        this.getConfigurationBeanBinder().bind(configurationProperties, ignoreUnknownFields, ignoreInvalidFields, configurationBean);
        if (this.log.isInfoEnabled()) {
            this.log.info("The configuration bean [" + configurationBean + "] have been binding by the configuration properties [" + configurationProperties + "]");
        }

    }

/**
 *  次数运用到了spring的属性绑定功能,不再深入细究。
 */
class BinderDubboConfigBinder implements ConfigurationBeanBinder {

    @Override
    public void bind(Map<String, Object> configurationProperties, boolean ignoreUnknownFields,
                     boolean ignoreInvalidFields, Object configurationBean) {

        Iterable<PropertySource<?>> propertySources = asList(new MapPropertySource("internal", configurationProperties));

        // Converts ConfigurationPropertySources
        Iterable<ConfigurationPropertySource> configurationPropertySources = from(propertySources);

        // Wrap Bindable from DubboConfig instance
        Bindable bindable = Bindable.ofInstance(configurationBean);

        Binder binder = new Binder(configurationPropertySources, new PropertySourcesPlaceholdersResolver(propertySources));

        // Get BindHandler
        BindHandler bindHandler = getBindHandler(ignoreUnknownFields, ignoreInvalidFields);

        // Bind
        binder.bind("", bindable, bindHandler);
    }

    private BindHandler getBindHandler(boolean ignoreUnknownFields,
                                       boolean ignoreInvalidFields) {
        BindHandler handler = BindHandler.DEFAULT;
        if (ignoreInvalidFields) {
            handler = new IgnoreErrorsBindHandler(handler);
        }
        if (!ignoreUnknownFields) {
            UnboundElementsSourceFilter filter = new UnboundElementsSourceFilter();
            handler = new NoUnboundElementsBindHandler(handler, filter);
        }
        return handler;
    }
}

到这里,属性绑定就成功完成了。

问题源码跟踪

说会一开始出现的问题,主要问题是 在启动的时候回去连接 zk ,启动是由 org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener#onApplicationContextEvent 触发 org.apache.dubbo.config.bootstrap.DubboBootstrap#start 方法,监听 spring 声明周期事件。

  1. org.apache.dubbo.config.bootstrap.DubboBootstrap#initialize
  2. org.apache.dubbo.config.bootstrap.DubboBootstrap#startConfigCenter
  3. org.apache.dubbo.config.bootstrap.DubboBootstrap#useRegistryAsConfigCenterIfNecessary(额外说明一下该方法,代码如下:)
  4. org.apache.dubbo.config.bootstrap.DubboBootstrap#prepareEnvironment
  5. org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory#getDynamicConfiguration
  6. org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory#createDynamicConfiguration
  7. org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory#createDynamicConfiguration (这里根据不同的实现类去到不同的方法,笔者这里是 zk)
  8. org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter#connect
  9. org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter#createZookeeperClient
  10. org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient#CuratorZookeeperClient
  11. org.apache.curator.framework.CuratorFramework#blockUntilConnected(int, java.util.concurrent.TimeUnit)
    private void useRegistryAsConfigCenterIfNecessary() {
        // we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated.
        if (environment.getDynamicConfiguration().isPresent()) {
            return;
        }

        if (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) {
            return;
        }

        configManager
                .getDefaultRegistries()
                .stream()
                .filter(this::isUsedRegistryAsConfigCenter)
                .map(this::registryAsConfigCenter)
                .forEach(configManager::addConfigCenter);
    }

    public List<RegistryConfig> getDefaultRegistries() {
        return getDefaultConfigs(getConfigsMap(getTagName(RegistryConfig.class)));
    }

从 useRegistryAsConfigCenterIfNecessary 方法中可以看出,启动时获取的是 RegistryConfig 的配置,所以我们 consumer.timeout 是不生效的。

总结

Dubbo 其实区分了很多的配置,这些配置也有默认值,在出现问题的时候可以查看一下自己的配置,有些关于时间的配置可以适当的延长,毕竟网络是不稳定的。

希望大家生活愉快!!