溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

apollo在spring-boot中的加載過程是什么

發(fā)布時(shí)間:2021-10-15 13:41:45 來源:億速云 閱讀:143 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“apollo在spring-boot中的加載過程是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

集成使用

1、添加 gradle 依賴

    implementation "com.ctrip.framework.apollo:apollo-client:1.6.0"

2、配置 application.properties

apollo 自身的配置共包含 9 項(xiàng),必要配置只有 3 項(xiàng),其他的都是可選的配置。apollo 在 spring-boot 環(huán)境下的配置命名和 System 參數(shù)的命名保持了一直,最終 spring 的配置會(huì)注入到 System 中,具體的邏輯下文分析。

必須配置
#應(yīng)用的ID
app.id = java-project
# apollo 的 config-service 服務(wù)發(fā)現(xiàn)地址
apollo.meta = http://apollo.meta
# 啟用 apollo
apollo.bootstrap.enabled = true
可選配置
# 在日志系統(tǒng)初始化前加載 apollo 配置
apollo.bootstrap.eagerLoad.enabled=true
# 加載的命名空間,默認(rèn)加載 application ,多個(gè)以逗號(hào)隔開
apollo.bootstrap.namespaces = application
# apollo 的安全拉取 secret 配置
apollo.accesskey.secret = xx
# 集群配置
apollo.cluster = hk
# 緩存路徑
apollo.cacheDir = /opt
# 是否保持和 apollo 配置頁面的配置順序一致
apollo.property.order.enable = true

加載過程解析

public class ApolloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> , EnvironmentPostProcessor, Ordered {
  public static final int DEFAULT_ORDER = 0;

  private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
  private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
  private static final String[] APOLLO_SYSTEM_PROPERTIES = {"app.id", ConfigConsts.APOLLO_CLUSTER_KEY,
      "apollo.cacheDir", "apollo.accesskey.secret", ConfigConsts.APOLLO_META_KEY, PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE};

  private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector.getInstance(ConfigPropertySourceFactory.class);

  private int order = DEFAULT_ORDER;

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

    if (!environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false)) {
      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);

    initialize(environment);
  }


  /**
   * Initialize Apollo Configurations Just after environment is ready.
   *
   * @param environment
   */
  protected void initialize(ConfigurableEnvironment environment) {

    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);
  }

  /**
   * To fill system properties from environment config
   */
  void initializeSystemProperty(ConfigurableEnvironment environment) {
    for (String propertyName : APOLLO_SYSTEM_PROPERTIES) {
      fillSystemPropertyFromEnvironment(environment, propertyName);
    }
  }

  private void fillSystemPropertyFromEnvironment(ConfigurableEnvironment environment, String propertyName) {
    if (System.getProperty(propertyName) != null) {
      return;
    }

    String propertyValue = environment.getProperty(propertyName);

    if (Strings.isNullOrEmpty(propertyValue)) {
      return;
    }

    System.setProperty(propertyName, propertyValue);
  }

  /**
   *
   * In order to load Apollo configurations as early as even before Spring loading logging system phase,
   * this EnvironmentPostProcessor can be called Just After ConfigFileApplicationListener has succeeded.
   *
   * <br />
   * The processing sequence would be like this: <br />
   * Load Bootstrap properties and application properties -----> load Apollo configuration properties ----> Initialize Logging systems
   *
   * @param configurableEnvironment
   * @param springApplication
   */
  @Override
  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) {
      initialize(configurableEnvironment);
    }

  }

  /**
   * @since 1.3.0
   */
  @Override
  public int getOrder() {
    return order;
  }

  /**
   * @since 1.3.0
   */
  public void setOrder(int order) {
    this.order = order;
  }
}

apollo 在 spring-boot 中的加載邏輯都在如上的代碼中了,代碼的關(guān)鍵是實(shí)現(xiàn)了兩個(gè) spring 生命周期的接口,

  • ApplicationContextInitializer

在被 ConfigurableApplicationContext.refresh()刷新之前初始化 ConfigurableApplicationContext 的回調(diào)接口。

  • EnvironmentPostProcessor

比 ApplicationContextInitializer 的加載時(shí)機(jī)還要提前,此時(shí) spring-boot 的日志系統(tǒng)還未初始化,

postProcessEnvironment 方法邏輯解析

  • 1、初始化 System 的配置,將 spring 上下文中的配置(環(huán)境變量、System 參數(shù)、application.properties) 拷貝到 System 配置中, 如果 System 已經(jīng)存在同名的配置則跳過,保證了 -D 設(shè)置的 System 參數(shù)的最高優(yōu)先級(jí)。但是也帶來了一個(gè)隱含的問題,默認(rèn),apollo 的配置設(shè)計(jì)支持從環(huán)境變量中取值,也遵循了環(huán)境變量大寫的規(guī)范,將 System 參數(shù)的 "." 換成 "_" 拼接,然后變成大寫。 比如 apollo.meta 對(duì)應(yīng)環(huán)境變量的 APOLLO_META。但是在 spring-boot 的環(huán)境下,因?yàn)?spring 的配置系統(tǒng)默認(rèn)也會(huì)加載環(huán)境變量的配置,最終在環(huán)境變量里配置 apollo.meta 也會(huì)生效。甚至比正確配置的 APOLLO_META 環(huán)境變量值的優(yōu)先級(jí)還高。

  • 2、根據(jù) apollo.bootstrap.eagerLoad.enabled 和 apollo.bootstrap.enabled 的配置來判斷是否在這個(gè)階段初始化 apollo。 postProcessEnvironment() 執(zhí)行的時(shí)候, 此時(shí)日志系統(tǒng)并未初始化,在這個(gè)階段加載 apollo,可以解決將日志配置托管到 apollo 里直接生效的問題。 帶來的問題是, 假如在這個(gè)階段的 apollo 加載出現(xiàn)問題,由于日志系統(tǒng)未初始化,看不到 apollo 的加載日志,不方便定位 apollo 的加載問題。 所以博主建議,如果有托管日志配置的場(chǎng)景,可以先不啟用 apollo.bootstrap.eagerLoad.enabled 的配置,等 apollo 集成完成后在啟用。

initialize 方法邏輯解析

  • 1、根據(jù) apollo.bootstrap.enabled 的配置來判斷,是否在這個(gè)階段初始化 apollo ,如果此時(shí) spring 上下文中已經(jīng)包含了 apollo 的 PropertySources,代表 apollo 已經(jīng) 初始化過,則直接 return 掉

  • 2、根據(jù) apollo.bootstrap.namespaces 的配置,默認(rèn)不配置為 "application" ,依次獲取對(duì)應(yīng)的 namespace 的配置, 并將配置使用 addFirst() 具有最高優(yōu)先級(jí)屬性源的設(shè)置方法, 添加到了 spring 的配置上下文中。這里解釋了為什么 apollo 的配置的優(yōu)先級(jí)最高,比 application.properties 中直接配置都要高, 這個(gè)優(yōu)先級(jí)的問題會(huì)經(jīng)常鬧烏龍,在本地開發(fā)調(diào)試階段,會(huì)直接在 application.properties 里調(diào)試配置,然后怎么改都不生效,因?yàn)?apollo 里 存在了同名的配置,啟動(dòng)的時(shí)候直接覆蓋了本地的配置。博主也犯過幾次這個(gè)錯(cuò)誤

“apollo在spring-boot中的加載過程是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI