【apollo在springboot中运行原理】

系列文章目录

Springboot源码分析之apollo配置


文章目录


前言

配置中心我相信有一年开发经验的程序员都听过吧,有三年开发经验的程序100%都使用过配置中心配置吧。apollo做为常用的配置中心,你知道它的原理吗?你知道它是怎么集成到springboot的吗?

本篇文章带你了解其原理,让你也能够自定义组件。


一、apollo是如何完成初始化的,拉取配置的呢

1. apollo的初始化阶段

Springboot环境准备阶段,发布ApplicationEnvironmentPreparedEvent 事件,EnvironmentPostProcessorApplicationListener 监听到事件之后 执行 postProcessEnvironment方法。apollo 中 的初始化类 ApolloApplicationContextInitializer 就实现EnvironmentPostProcessor接口。
ApolloApplicationContextInitializer

java 复制代码
  public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {

    // should always initialize system properties like app.id in the first place
    initializeSystemProperty(configurableEnvironment);

    Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);

    //EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
    if (!eagerLoadEnabled) {
      return;
    }

    Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);

    if (bootstrapEnabled) {
      DeferredLogger.enable();
      //重点实现逻辑的方法
      initialize(configurableEnvironment);
    }

  }

2.初始化RemoteConfigRepository

经过initialize()方法层层调用最后进入到RemoteConfigRepository init

java 复制代码
  public RemoteConfigRepository(String namespace) {
    m_namespace = namespace;
    m_configCache = new AtomicReference<>();
    m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
    m_httpClient = ApolloInjector.getInstance(HttpClient.class);
    m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
    remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
    m_longPollServiceDto = new AtomicReference<>();
    m_remoteMessages = new AtomicReference<>();
    m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());
    m_configNeedForceRefresh = new AtomicBoolean(true);
    m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),
        m_configUtil.getOnErrorRetryInterval() * 8);
    //下面三个方法就是拉去配置的核心代码了  
    //同步远程配置 
    this.trySync();
    //固定频率刷新、内部实现也是调用trySync()
    this.schedulePeriodicRefresh();
    //轮询访问远程配置中心,每次访问直到超时或者apollo 配置发生变化
    this.scheduleLongPollingRefresh();
  }

3.同步远程配置

AbstractConfigRepository.trySync()同步远程配置方法,方法内部主要实现逻辑在sync()

java 复制代码
protected synchronized void sync() {
    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");

    try {
      //m_configCache 
      ApolloConfig previous = m_configCache.get();
      //加载 远程 apollo config
      ApolloConfig current = loadApolloConfig();

      //reference equals means HTTP 304
      if (previous != current) {
        logger.debug("Remote Config refreshed!");
        m_configCache.set(current);
     	//getConfig 方法 将apollo配置放进 properties中
        this.fireRepositoryChange(m_namespace, this.getConfig());
      }

      if (current != null) {
        Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
            current.getReleaseKey());
      }

      transaction.setStatus(Transaction.SUCCESS);
    } catch (Throwable ex) {
      transaction.setStatus(ex);
      throw ex;
    } finally {
      transaction.complete();
    }
  }

private volatile AtomicReference<ApolloConfig> m_configCache; 使用了volatile 修饰,原子引用类型进行封装引用,这就是看源码的意义吧

4.发布ConfigFileChangeEvent事件

sync() 方法中的 调用fireRepositoryChange(),最后会调用到AbstractConfigFile.fireConfigChange()方法

java 复制代码
  private void fireConfigChange(final ConfigFileChangeEvent changeEvent) {
    for (final ConfigFileChangeListener listener : m_listeners) {
      m_executorService.submit(new Runnable() {
        @Override
        public void run() {
          String listenerName = listener.getClass().getName();
          Transaction transaction = Tracer.newTransaction("Apollo.ConfigFileChangeListener", listenerName);
          try {
          	//listner 是一个PropertySourcesProcessor 中的一个lambda 类,
          	//ConfigChangeListener configChangeEventPublisher = changeEvent ->
        //applicationEventPublisher.publishEvent(new ApolloConfigChangeEvent(changeEvent));
            listener.onChange(changeEvent);
            transaction.setStatus(Transaction.SUCCESS);
          } catch (Throwable ex) {
            transaction.setStatus(ex);
            Tracer.logError(ex);
            logger.error("Failed to invoke config file change listener {}", listenerName, ex);
          } finally {
            transaction.complete();
          }
        }
      });
    }
  }

因此我们可以监听ApolloConfigChangeEvent事件来监听apollo配置是否发生产变化

eg:

java 复制代码
public class ListenerApollo implements ConfigChangeListener, ApplicationListener<ApolloConfigChangeEvent> {
  @Override
  public void onChange(ConfigChangeEvent configChangeEvent) {
    //核心线程配置发生变化、重新set
    ConfigChange coreSize = configChangeEvent.getChange("threadPool.corePoolSize");
    if( coreSize!= null){
      ThreadPoolExecutor executor = SpringUtil.getBean(ThreadPoolExecutor.class);
      executor.setCorePoolSize(Integer.valueOf(coreSize.getNewValue()));
    }
  }

  @Override
  public void onApplicationEvent(ApolloConfigChangeEvent event) {
    this.onChange(event.getConfigChangeEvent());
  }
}

总结

分析apollo在Springboot启动的哪个环节被集成初始化的,以及跟了同步远程配置的逻辑,初始化时同步一次,定时同步,长轮询监听配置是否发生变化,发生变化又通知程序进行同步。并且同步后也会发布一个ApolloConfigChangeEvent事件,放开发者去监听配置变化。

相关推荐
橘子编程1 分钟前
Tomcat全栈指南:从入门到精通
java·tomcat
医疗信息化王工4 分钟前
基于ASP.NET Core的住院日志统计系统设计与实现
后端·layui·asp.net core·npoi·dapper
卜夋5 分钟前
Rust学习 - 变量与类型
后端
hudson20226 分钟前
work_mem: 这是一个陷阱!
后端·postgresql
hrhcode6 分钟前
【java工程师快速上手go】三.Go Web开发(Gin框架)
java·spring boot·golang
敖正炀6 分钟前
CountDownLatch 详解
java
Nturmoils7 分钟前
实时决策时代,工业物联网需要什么样的数据库?
数据库·后端
海兰8 分钟前
【Spring AI】从一个MCP小实例开始
java·人工智能·spring
用户8356290780519 分钟前
Python 实现 Word 页眉页脚添加与自定义设置
后端·python
Rick199316 分钟前
Spring Boot自动装配原理
java·spring boot·后端