apollo在Springboot如何运行的

前言

配置中心我相信有一年开发经验的程序员都听过吧,有三年开发经验的程序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事件,放开发者去监听配置变化。

相关推荐
计算机学姐33 分钟前
基于python+django+vue的影视推荐系统
开发语言·vue.js·后端·python·mysql·django·intellij-idea
小筱在线38 分钟前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
JustinNeil42 分钟前
简化Java对象转换:高效实现大对象的Entity、VO、DTO互转与代码优化
后端
luoluoal43 分钟前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea1 小时前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
小游鱼KF1 小时前
Spring学习前置知识
java·学习·spring
扎克begod1 小时前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
青灯文案11 小时前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
我就是程序猿1 小时前
tomcat的配置
java·tomcat
阳光阿盖尔1 小时前
EasyExcel的基本使用——Java导入Excel数据
java·开发语言·excel