1. 程式人生 > >Springboot 通過Apollo-client載入配置的過程

Springboot 通過Apollo-client載入配置的過程

springboot 通過Apollo-client載入配置的過程

1.Apollo-client利用springboot的META-INF/spring.factories將ApolloApplicationContextInitializer新增到springboot的ApplicationContextInitializer

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer

2.springboot啟動時.經過前面的資源收集.然後載入ApplicationContextInitializer.即執行ApolloApplicationContextInitializer.java中initialize().此類為apollo入口類

@Override
  public void initialize(ConfigurableApplicationContext context) {
    ConfigurableEnvironment environment = context.getEnvironment();

    initializeSystemProperty(environment);

    String enabled = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "false");
    if (!Boolean.valueOf(enabled)) {
      logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
      return;
    }
    logger.debug("Apollo bootstrap config is enabled for context {}", context);

    if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
      //already initialized
      return;
    }

    String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
    logger.debug("Apollo bootstrap namespaces: {}", namespaces);
    List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

    CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
    for (String namespace : namespaceList) {
      Config config = ConfigService.getConfig(namespace);

      composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
    }

    environment.getPropertySources().addFirst(composite);
  }

3.方法中進入Config config = ConfigService.getConfig(namespace);

 4.ConfigService.java

public static Config getConfig(String namespace) {
  return s_instance.getManager().getConfig(namespace);
}

5.進入DefaultConfigManager

@Override
public Config getConfig(String namespace) {
  Config config = m_configs.get(namespace);

  if (config == null) {
    synchronized (this) {
      config = m_configs.get(namespace);

      if (config == null) {
        ConfigFactory factory = m_factoryManager.getFactory(namespace);
//斷點2 開始去獲取config
        config = factory.create(namespace);
        m_configs.put(namespace, config);
      }
    }
  }

  return config;
}

6.DefaultConfigFactory.java....進入createLocalConfigRepository(namespace)

@Override
public Config create(String namespace) {
  DefaultConfig defaultConfig = new DefaultConfig(namespace, createLocalConfigRepository(namespace));
  return defaultConfig;
}

進去.然後createRemoteConfigRepository(namespace)---->new RemoteConfigRepository(namespace);

LocalFileConfigRepository createLocalConfigRepository(String namespace) {
  if (m_configUtil.isInLocalMode()) {
    logger.warn(
        "==== Apollo is in local mode! Won't pull configs from remote server for namespace {} ! ====",
        namespace);
    return new LocalFileConfigRepository(namespace);
  }
//斷點8
  return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));
}

RemoteConfigRepository createRemoteConfigRepository(String namespace) {
  return new RemoteConfigRepository(namespace);
}

7.RemoteConfigRepository.java--->進入this.trySync();

public RemoteConfigRepository(String namespace) {
  m_namespace = namespace;
  m_configCache = new AtomicReference<>();
  m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
  m_httpUtil = ApolloInjector.getInstance(HttpUtil.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);
  gson = new Gson();
  this.trySync();
  this.schedulePeriodicRefresh();
  this.scheduleLongPollingRefresh();
}

8.RemoteConfigRepository.java------>loadApolloConfig();

@Override
protected synchronized void sync() {
  Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");

  try {
    ApolloConfig previous = m_configCache.get();
//斷點10
    ApolloConfig current = loadApolloConfig();

    //reference equals means HTTP 304
    if (previous != current) {
      logger.debug("Remote Config refreshed!");
      m_configCache.set(current);
      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();
  }
}

繼續進入loadApolloConfig.

private ApolloConfig loadApolloConfig() {
  if (!m_loadConfigRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) {
    //wait at most 5 seconds
    try {
      TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
    }
  }
  String appId = m_configUtil.getAppId();
  String cluster = m_configUtil.getCluster();
  String dataCenter = m_configUtil.getDataCenter();
  Tracer.logEvent("Apollo.Client.ConfigMeta", STRING_JOINER.join(appId, cluster, m_namespace));
  int maxRetries = m_configNeedForceRefresh.get() ? 2 : 1;
  long onErrorSleepTime = 0; // 0 means no sleep
  Throwable exception = null;
  //斷點11 此處進入獲取元服務資訊
  List<ServiceDTO> configServices = getConfigServices();
  String url = null;
  for (int i = 0; i < maxRetries; i++) {
    List<ServiceDTO> randomConfigServices = Lists.newLinkedList(configServices);
    Collections.shuffle(randomConfigServices);
    //Access the server which notifies the client first
    if (m_longPollServiceDto.get() != null) {
      randomConfigServices.add(0, m_longPollServiceDto.getAndSet(null));
    }

    for (ServiceDTO configService : randomConfigServices) {
      if (onErrorSleepTime > 0) {
        logger.warn(
            "Load config failed, will retry in {} {}. appId: {}, cluster: {}, namespaces: {}",
            onErrorSleepTime, m_configUtil.getOnErrorRetryIntervalTimeUnit(), appId, cluster, m_namespace);

        try {
          m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(onErrorSleepTime);
        } catch (InterruptedException e) {
          //ignore
        }
      }
//生成訪問元服務的url
//http://10.10.10.56:8080/configs/spring-boot-logger/default/application?ip=10.10.10.56
      url = assembleQueryConfigUrl(configService.getHomepageUrl(), appId, cluster, m_namespace,
              dataCenter, m_remoteMessages.get(), m_configCache.get());

      logger.debug("Loading config from {}", url);
      HttpRequest request = new HttpRequest(url);

      Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "queryConfig");
      transaction.addData("Url", url);
      try {
//將url訪問元服務
        HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class);
        m_configNeedForceRefresh.set(false);
        m_loadConfigFailSchedulePolicy.success();

        transaction.addData("StatusCode", response.getStatusCode());
        transaction.setStatus(Transaction.SUCCESS);

        if (response.getStatusCode() == 304) {
          logger.debug("Config server responds with 304 HTTP status code.");
          return m_configCache.get();
        }
//獲取元服務返回的結果資料
        ApolloConfig result = response.getBody();

        logger.debug("Loaded config for {}: {}", m_namespace, result);

        return result;
      } catch (ApolloConfigStatusCodeException ex) {
//省略
       }
         
}
9.回到斷點10...

10.回到//斷點8...

LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));

11一路退出..

config = factory.create(namespace);

12一路退出..

Config config = ConfigService.getConfig(namespace);13一路退出..
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));

14.多個namespace多次迴圈

15最後將配置放到environment.PropertySources

environment.getPropertySources().addFirst(composite);