Apollo配置中心動態生效實現原理


https://blog.csdn.net/Rongbo_J/article/details/93379683

 

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); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

那么@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; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

如上面代碼所示,在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<String, Object> 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); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

如上面代碼所示,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:\opt\data 或者/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對象相關信息是不能動態修改的,所以依然需要重啟應用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM