使用Netflix Archaius進(jìn)行配置管理

在這篇文章中,我們將討論Archaius,一個(gè)非常酷且易于使用的Netflix配置管理工具。

通常我們都是如何讀取配置變量的呢?

一種是使用System.getProperty()方法獲得JVM系統(tǒng)屬性。例如下面這樣:

String prop = System.getProperty("myProperty");
int x = DEFAULT_VALUE;
try {
  x = Integer.parseInt(prop);
} catch (NumberFormatException e) {
  // handle format issues
}
myMethod(x);

您還可以使用Spring讀取屬性文件 ?;蛘吣愕臄?shù)據(jù)庫(kù)中有一個(gè)簡(jiǎn)單的鍵/值表,你可以從那里讀取一些屬性。又或者您從外部REST端點(diǎn)獲取它們。除此之外還可以從其他類(lèi)型的鍵/值存儲(chǔ)中獲取,比如Redis或Memcached。

無(wú)論情況如何,您的配置變量可能來(lái)自許多不同的來(lái)源,特別是如果您的應(yīng)用程序使用多個(gè),這可能會(huì)變得難以維護(hù)。另一個(gè)重要的問(wèn)題,您不希望每次更改其中一個(gè)屬性的值時(shí)需要重新部署,特別是在持續(xù)集成的時(shí)候。

為解決這些問(wèn)題,Netflix提出了一個(gè)開(kāi)源的解決方案:Archaius。Archaius是Apache公共配置庫(kù)的擴(kuò)展, 它允許您從多個(gè)動(dòng)態(tài)源中檢索屬性,并且它解決了前面提到的所有問(wèn)題(異構(gòu)的屬性源,運(yùn)行時(shí)更改等)。

我們先從用Archaius讀取屬性文件最簡(jiǎn)單的示例開(kāi)始。

public class ApplicationConfig {

  public String getStringProperty(String key, String defaultValue) {
    final DynamicStringProperty property = DynamicPropertyFactory.getInstance().getStringProperty(key,
        defaultValue);
    return property.get();
  }
}

public class ApplicationConfigTest {
  private ApplicationConfig appConfig = new ApplicationConfig();

  @Test
  public void shouldRetrieveThePropertyByKey() {
    String property = appConfig.getStringProperty("hello.world.message", "default message");

    assertThat(property, is("Hello Archaius World!"));
  }

  @Test
  public void shouldRetrieveDefaultValueWhenKeyIsNotPresent() {
    String property = appConfig.getStringProperty("some.key", "default message");

    assertThat(property, is("default message"));
  }
}

該代碼是讀取類(lèi)路徑中某處的“config.properties”文件。請(qǐng)注意,您不需要告訴Archaius在哪里找到您的屬性文件,因?yàn)樗檎业哪J(rèn)名稱(chēng)是“config.properties”。

如果您不想或不能將屬性文件命名為“config.property”,該怎么辦?在這種情況下,您需要告訴Archaius在哪里查找此文件。您可以輕松地更改系統(tǒng)屬性'archaius.configurationSource.defaultFileName',在啟動(dòng)應(yīng)用程序時(shí)將其作為參數(shù)傳遞給vm:

java ... -Darchaius.configurationSource.defaultFileName=customName.properties

或者寫(xiě)在代碼本身中:

public class ApplicationConfig {
  static {
    System.setProperty("archaius.configurationSource.defaultFileName", "customConfig.properties");
  }

  public String getStringProperty(String key, String defaultValue) {
    final DynamicStringProperty property = DynamicPropertyFactory.getInstance().getStringProperty(key,
        defaultValue);
    return property.get();
  }
}

現(xiàn)在,如果你想讀幾個(gè)屬性文件怎么辦?您可以從首先加載的默認(rèn)文件開(kāi)始,輕松定義屬性文件鏈及其加載順序。從那里,您可以使用鍵“@ next = nextFile.properties”指定一個(gè)特殊屬性來(lái)告訴Archaius哪個(gè)是應(yīng)該加載的下一個(gè)文件。

在我們的示例中,我們可以在“customConfig.properties”文件中添加以下行:

@next=secondConfig.properties

并將相應(yīng)的“secondConfig.properties”添加到我們的resources文件夾中,其中包含以下內(nèi)容:

cascade.property=cascade value

我們可以通過(guò)在ApplicationConfigTest類(lèi)中添加以下測(cè)試來(lái)驗(yàn)證:

@Test
public void shouldReadCascadeConfigurationFiles() {
    String property = appConfig.getStringProperty("cascade.property", "not found");

    assertThat(property, is("cascade value"));
}

請(qǐng)注意,我們從新文件中獲取屬性,而不對(duì)ApplicationConfig類(lèi)進(jìn)行任何其他更改。從客戶的角度來(lái)看,這是完全透明的。

到目前為止,我們一直在從不同的屬性文件中讀取屬性,但如果您想從不同的源讀取它們會(huì)怎么樣?在最常見(jiàn)的情況下,您可以通過(guò)實(shí)現(xiàn)“com.netflix.config.PolledConfigurationSource”來(lái)編寫(xiě)自己的邏輯。如果新源是可以通過(guò)JDBC訪問(wèn)的數(shù)據(jù)庫(kù),那么Archaius已經(jīng)提供了可以使用的“JDBCConfigurationSource”。您只需要告訴他應(yīng)該使用什么查詢來(lái)獲取屬性以及哪些列表示屬性鍵和屬性值。

我們的例子如下:

@Component
public class ApplicationConfig {
  static {
    System.setProperty("archaius.configurationSource.defaultFileName", "customConfig.properties");
  }

  private final DataSource dataSource;

  @Autowired
    public ApplicationConfig(DataSource dataSource) {
      this.dataSource = dataSource;
      installJdbcSource();
    }

  public String getStringProperty(String key, String defaultValue) {
    final DynamicStringProperty property = DynamicPropertyFactory.getInstance().getStringProperty(key,
        defaultValue);
    return property.get();
  }

  private DynamicConfiguration installJdbcSource() {
    if (!isConfigurationInstalled()) {
        PolledConfigurationSource source = new JDBCConfigurationSource(dataSource,
                "select distinct property_key, property_value from properties", "property_key", "property_value");
        DynamicConfiguration configuration = new DynamicConfiguration(source,
                new FixedDelayPollingScheduler(0, 10000, true));

        ConfigurationManager.install(configuration);
        return configuration;
    }
    return null;
  }
}

我們使用Spring來(lái)自動(dòng)裝配數(shù)據(jù)源,該數(shù)據(jù)源將使用具有簡(jiǎn)單鍵/值表的基于內(nèi)存的H2數(shù)據(jù)庫(kù)。注意我們是如何創(chuàng)建一個(gè)新的 PolledConfigurationSource的,使用Archaius已經(jīng)提供的JDBCConfigurationSource,然后我們注冊(cè)使用新的配置 ConfigurationManager。執(zhí)行此操作后,我們可以從數(shù)據(jù)庫(kù)中獲取任何屬性,就像我們對(duì)屬性文件所做的那樣(即使用 DynamicPropertyFactory)。

我們現(xiàn)在可以添加幾個(gè)測(cè)試類(lèi)來(lái)確保我們實(shí)際上從數(shù)據(jù)庫(kù)中讀取屬性,并且我們可以更新它們的值并查看動(dòng)態(tài)配置中反映的更改。

@Test
public void shouldRetrievePropertyFromDB() {
    String property = appConfig.getStringProperty("db.property", "default message");

    assertThat(property, is("this is a db property"));
}

@Test
public void shouldReadTheNewValueAfterTheSpecifiedDelay() throws InterruptedException, SQLException {
    template.update("update properties set property_value = 'changed value' where property_key = 'db.property'");

    String propertyValue = (String) template.queryForObject(
            "select property_value from properties where property_key = 'db.property'", java.lang.String.class);
    System.out.println(propertyValue);

    String property = appConfig.getStringProperty("db.property", "default message");

    // We updated the value on the DB but Archaius polls for changes every 10000
    // milliseconds so it still sees the old value
    assertThat(property, is("this is a db property"));

    Thread.sleep(30000);

    property = appConfig.getStringProperty("db.property", "default message");
    assertThat(property, is("changed value"));
}

Archaius提供的另一個(gè)非??岬墓δ苁强梢酝ㄟ^(guò)JMX 將我們的配置注冊(cè)為 MBean。我們可以默認(rèn)設(shè)置系統(tǒng)屬性 archaius.dynamicPropertyFactory.registerConfigWithJMX = true或使用ConfigJMXManager.registerConfigMbean(config)進(jìn)行編程。

執(zhí)行此操作后,我們可以通過(guò)JConsole連接,不僅可以獲取所有屬性的值,還可以更新它們并查看它們?cè)贏rchaius中反映的新值。例如,這將允許我們?cè)谶\(yùn)行時(shí)更改屬性文件中靜態(tài)定義的屬性值,而無(wú)需服務(wù)器推送。我們可以稍微修改一下ApplicationConfig類(lèi)來(lái)添加一個(gè)main方法,該方法將持續(xù)運(yùn)行打印不同屬性的值,這樣將允許我們?cè)贘Console中使用。

public class ApplicationConfig extends Thread {

  private final DataSource dataSource;

  @Autowired
    public ApplicationConfig(DataSource dataSource) {
      this.dataSource = dataSource;
      cascadeDefaultConfiguration();
      DynamicConfiguration jdbcSource = installJdbcSource();
      registerMBean(jdbcSource);
    }

  public String getStringProperty(String key, String defaultValue) {
    final DynamicStringProperty property = DynamicPropertyFactory.getInstance().getStringProperty(key,
        defaultValue);
    return property.get();
  }

  @Override
    public void run() {
      while (true) {
        try {
          sleep(100);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }

      }
    }

  private void registerMBean(DynamicConfiguration jdbcSource) {
    setDaemon(false);
    ConfigJMXManager.registerConfigMbean(jdbcSource);
  }

  public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("archaiusContext.xml");
    ApplicationConfig applicationConfig = (ApplicationConfig) applicationContext.getBean("applicationConfig");

    applicationConfig.start();

    while (true) {
      try {
        System.out.println(applicationConfig.getStringProperty("hello.world.message", "default message"));
        System.out.println(applicationConfig.getStringProperty("cascade.property", "default message"));
        System.out.println(applicationConfig.getStringProperty("db.property", "default message"));
        sleep(3000);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }

    }
  }
}

您還可以使用Archaius進(jìn)行更多操作,例如每次屬性更改時(shí)的回調(diào),與Zookeeper或其他服務(wù)的集成。您可以在GitHub上找到本文中顯示的所有代碼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,288評(píng)論 6 342
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,688評(píng)論 1 32
  • 昨天園長(zhǎng)特意給我們老師開(kāi)會(huì),表情十分嚴(yán)肅地特別強(qiáng)調(diào)老師的師德。 作為一名幼兒園老師只能陪孩子度過(guò)幼兒園的美好時(shí)光,...
    清清的海閱讀 1,267評(píng)論 14 18
  • 2018年2月9號(hào) 天氣晴 今天和往常一樣早上起來(lái)給兒子做好飯,吃了去送他學(xué)思維導(dǎo)圖,老師說(shuō)今天上午最后一節(jié)課,下...
    和兒子一起成長(zhǎng)閱讀 400評(píng)論 0 2

友情鏈接更多精彩內(nèi)容