APP下载

Apollo配置中心动态生效实现原理

消息来源:baojiabao.com 作者: 发布时间:2024-05-16

报价宝综合消息Apollo配置中心动态生效实现原理

Apollo配置中心动态生效原理

自定义BeanFactoryPostProcessor

自定义BeanPostProcessor

总结

Spring中的重要概念

在了解Apollo配置中心实现原理之前,我们需要先熟悉一下Spring框架中的几个重要的概念:

1、BeanDefinition

用于描述Bean的配置资讯,Bean配置一般有三种方式:

(1)XML配置档案

(2)@Service、@Component等注解

(3)Java Config方式

对应的BeanDefinition实现类如下图,Spring容器启动时,会把所有的Bean配置资讯转换为BeanDefinition物件。

2、BeanDefinitionRegistry

BeanDefinition容器,所有的Bean定义都注册在BeanDefinitionRegistry物件中。

3、PropertySource

用于存放Spring配置资源资讯,例如spring专案中properties或者yaml档案配置资讯均会储存在PropertySource物件中。Spring支援使用@PropertySource注解,将配置资讯载入到Environment物件中。

4、ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar是一个界面,该界面的实现类作用于在Spring解析Bean配置生成BeanDefinition物件阶段。

在Spring解析Configuration注解时,向Spring容器中增加额外的BeanDefinition。

5、BeanFactoryPostProcessor

Bean工厂后置处理器,用于在BeanDefinition物件注册完成后,修改Bean工厂资讯,例如增加或者修改BeanDefinition物件。

6、BeanDefinitionRegistryPostProcessor

它是一个特殊的BeanFactoryPostProcessor,用于在BeanDefinition物件注册完成后,访问、新增或者修改BeanDefinition资讯。

7、PropertySourcesPlaceholderConfigurer

它是一个特殊的BeanFactoryPostProcessor,用于解析Bean配置中的${…}引数占位符。

8、BeanPostProcessor

Bean后置处理器,bean初始化方法呼叫前后,执行拦截逻辑,可以对原有的Bean进行包装或者根据标记界面建立代理物件。

Spring框架启动过程回顾

Spring框架启动大致会经过以下几个阶段:

1、解析Bean配置资讯,将配置资讯转换为BeanDefinition物件,注册到BeanDefinitionRegistry中。

2、执行所有的BeanFactoryPostProcessor的postProcessBeanFactory()方法对Bean工厂资讯进行修改,包括修改或新增BeanDefinition物件。

注意:如果需要控制BeanFactoryPostProcessor的执行顺序需要实现PriorityOrdered界面,getOrder()方法返回的值越小,执行优先级越高。

3、通过BeanDefinition物件例项化所有Bean,注入依赖。

4、执行所有BeanPostProcessor物件的postProcessBeforeInitialization()方法。

5、执行Bean的初始化方法,例如InitializingBean界面的afterPropertiesSet方法,或init-method属性指定的方法。

执行所有BeanPostProcessor物件的postProcessAfterInitialization()方法

Apollo原理解析

Apollo框架使用非常简单,如果是Spring Boot专案,只需要在启动类上增加@EnableApolloConfig注解即可。例如:

@SpringBootApplication

@EnableApolloConfig

public class Application {

public static void main(String[] args) {

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

SpringApplication.run(Application.class, args);

}

}

那么@EnableApolloConfig注解到底做了什么事情了,我们可以看下EnableApolloConfig注解的定义,程式码如下:

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Documented

@Import(ApolloConfigRegistrar.class)

public @interface EnableApolloConfig {

/**

* Apollo namespaces to inject configuration into Spring Property Sources.

*/

String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};

/**

* The order of the apollo config, default is {@link Ordered#LOWEST_PRECEDENCE}, which is Integer.MAX_VALUE.

* If there are properties with the same name in different apollo configs, the apollo config with smaller order wins.

* @return

*/

int order() default Ordered.LOWEST_PRECEDENCE;

}

如上面程式码所示,在EnableApolloConfig注解中,通过@Import注解汇入了一个ApolloConfigRegistrar,接下来我们就来看一下ApolloConfigRegistrar的实现:

public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {

@Override

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata

.getAnnotationAttributes(EnableApolloConfig.class.getName()));

String[] namespaces = attributes.getStringArray("value");

int order = attributes.getNumber("order");

PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);

Map propertySourcesPlaceholderPropertyValues = new HashMap();

// to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer

propertySourcesPlaceholderPropertyValues.put("order", 0);

BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),

PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);

BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),

PropertySourcesProcessor.class);

BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),

ApolloAnnotationProcessor.class);

BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);

BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(), SpringValueDefinitionProcessor.class);

BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(),

ApolloJsonValueProcessor.class);

}

}

如上面程式码所示,ApolloConfigRegistrar实现了ImportBeanDefinitionRegistrar界面,前面有提到过,ImportBeanDefinitionRegistrar界面的实现类作用于在Spring解析Bean配置生成BeanDefinition物件阶段,在Spring解析Configuration注解时,向Spring容器中增加额外的BeanDefinition。

ApolloConfigRegistrar中注册了几个BeanDefinition,具体如下:

1、PropertySourcesPlaceholderConfigurer -------->BeanFactoryPostProcessor

2、PropertySourcesProcessor -------->BeanFactoryPostProcessor

3、ApolloAnnotationProcessor -------->BeanPostProcessor

4、SpringValueProcessor -------->BeanFactoryPostProcessor和BeanPostProcessor

5、SpringValueDefinitionProcessor-------->BeanDefinitionRegistryPostProcessor

(即BeanFactoryPostProcessor)

6、ApolloJsonValueProcessor -------->BeanPostProcessor

这些类要么实现了BeanFactoryPostProcessor界面,要么实现了BeanPostProcessor界面,前面有提到过BeanFactoryPostProcessor和BeanPostProcessor是Spring提供的扩充套件机制,BeanFactoryPostProcessor一定是在BeanPostProcessor之前执行。

接下来我们就来看一下这些自定义的BeanFactoryPostProcessor和BeanPostProcessor的执行顺序,以及它们具体做了什么事情。

自定义BeanFactoryPostProcessor

1、SpringValueDefinitionProcessor

对所有的BeanDefinition进行遍历,将属性中包含${…}引数占位符的属性新增到Apollo 属性登录档。Apollo 属性登录档具体结构如下:

2、PropertySourcesProcessor

(1)根据名称空间从配置中心获取配置资讯,建立RemoteConfigRepository和LocalFileConfigRepository物件。RemoteConfigRepository表示远端配置中心资源,LocalFileConfigRepository表示本地快取配置资源。

(2)LocalFileConfigRepository物件快取配置资讯到C:optdata 或者/opt/data目录。

(3)RemoteConfigRepository开启HTTP长轮询请求定时任务,预设2s请求一次。

(4)将本地快取配置资讯转换为PropertySource物件(Apollo自定义了Spring的PropertySource),载入到Spring的Environment物件中。

(5)将自定义的ConfigPropertySource注册为观察者。一旦RemoteConfigRepository发现远端配置中心资讯发生变化,ConfigPropertySource物件会得到通知。

3、PropertySourcesPlaceholderConfigurer

载入本地Properties档案,将${…}引数占位符替换为具体的值。

4、SpringValueProcessor

仅仅是为了获取SpringValueDefinitionProcessor中获取的 包含${…}引数占位符的BeanDefinition。(从面向物件设计原则的角度,不符合单一责任原则,可以注册到Guice容器里,然后从Guice容器获取。)

自定义BeanPostProcessor

5、ApolloJsonValueProcessor

处理ApolloJsonValue注解,属性或者方法中包含ApolloJsonValue注解的Bean,属性值也会根据配置中心配置的修改发生变化,因此也需要新增到配置中心可配的容器中

6、ApolloAnnotationProcessor

处理ApolloConfigChangeListener注解,ApolloConfigChangeListener注解用于注册一个配置变化监听器。

7、SpringValueProcessor

处理Spring中的Value注解,将属性或者方法中包含Value注解的Bean资讯新增到Apollo属性登录档。

整个过程如下图所示:

总结

Apollo配置中心动态生效机制,是基于Http长轮询请求和Spring扩充套件机制实现的,在Spring容器启动过程中,Apollo通过自定义的BeanPostProcessor和BeanFactoryPostProcessor将引数中包含${…}占位符和@Value注解的Bean注册到Apollo框架中定义的登录档中。然后通过Http长轮询不断的去获取服务端的配置资讯,一旦配置发生变化,Apollo会根据变化的配置的Key找到对应的Bean,然后修改Bean的属性,从而实现了配置动态生效的特性。

需要注意的是,Apollo在配置变化后,只能修改Bean的属性,例如我们资料来源的属性发生变化,新建立的Connection物件是没问题的,但是连线池中已经建立的Connection物件相关资讯是不能动态修改的,所以依然需要重启应用。

2019-07-30 17:48:00

相关文章