使用Netflix Archaius进行配置管理

在这篇文章中,我们将讨论Archaius,一个非常酷且易于使用的Netflix配置管理工具。

通常我们都是如何读取配置变量的呢?

一种是使用System.getProperty()方法获得JVM系统属性。例如下面这样:

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

您还可以使用Spring读取属性文件 。或者你的数据库中有一个简单的键/值表,你可以从那里读取一些属性。又或者您从外部REST端点获取它们。除此之外还可以从其他类型的键/值存储中获取,比如Redis或Memcached。

无论情况如何,您的配置变量可能来自许多不同的来源,特别是如果您的应用程序使用多个,这可能会变得难以维护。另一个重要的问题,您不希望每次更改其中一个属性的值时需要重新部署,特别是在持续集成的时候。

为解决这些问题,Netflix提出了一个开源的解决方案:Archaius。Archaius是Apache公共配置库的扩展, 它允许您从多个动态源中检索属性,并且它解决了前面提到的所有问题(异构的属性源,运行时更改等)。

我们先从用Archaius读取属性文件最简单的示例开始。

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

该代码是读取类路径中某处的“config.properties”文件。请注意,您不需要告诉Archaius在哪里找到您的属性文件,因为他要查找的默认名称是“config.properties”。

如果您不想或不能将属性文件命名为“config.property”,该怎么办?在这种情况下,您需要告诉Archaius在哪里查找此文件。您可以轻松地更改系统属性'archaius.configurationSource.defaultFileName',在启动应用程序时将其作为参数传递给vm:

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

或者写在代码本身中:

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

现在,如果你想读几个属性文件怎么办?您可以从首先加载的默认文件开始,轻松定义属性文件链及其加载顺序。从那里,您可以使用键“@ next = nextFile.properties”指定一个特殊属性来告诉Archaius哪个是应该加载的下一个文件。

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

@next=secondConfig.properties

并将相应的“secondConfig.properties”添加到我们的resources文件夹中,其中包含以下内容:

cascade.property=cascade value

我们可以通过在ApplicationConfigTest类中添加以下测试来验证:

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

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

请注意,我们从新文件中获取属性,而不对ApplicationConfig类进行任何其他更改。从客户的角度来看,这是完全透明的。

到目前为止,我们一直在从不同的属性文件中读取属性,但如果您想从不同的源读取它们会怎么样?在最常见的情况下,您可以通过实现“com.netflix.config.PolledConfigurationSource”来编写自己的逻辑。如果新源是可以通过JDBC访问的数据库,那么Archaius已经提供了可以使用的“JDBCConfigurationSource”。您只需要告诉他应该使用什么查询来获取属性以及哪些列表示属性键和属性值。

我们的例子如下:

@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来自动装配数据源,该数据源将使用具有简单键/值表的基于内存的H2数据库。注意我们是如何创建一个新的 PolledConfigurationSource的,使用Archaius已经提供的JDBCConfigurationSource,然后我们注册使用新的配置 ConfigurationManager。执行此操作后,我们可以从数据库中获取任何属性,就像我们对属性文件所做的那样(即使用 DynamicPropertyFactory)。

我们现在可以添加几个测试类来确保我们实际上从数据库中读取属性,并且我们可以更新它们的值并查看动态配置中反映的更改。

@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提供的另一个非常酷的功能是可以通过JMX 将我们的配置注册为 MBean。我们可以默认设置系统属性 archaius.dynamicPropertyFactory.registerConfigWithJMX = true或使用ConfigJMXManager.registerConfigMbean(config)进行编程。

执行此操作后,我们可以通过JConsole连接,不仅可以获取所有属性的值,还可以更新它们并查看它们在Archaius中反映的新值。例如,这将允许我们在运行时更改属性文件中静态定义的属性值,而无需服务器推送。我们可以稍微修改一下ApplicationConfig类来添加一个main方法,该方法将持续运行打印不同属性的值,这样将允许我们在JConsole中使用。

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进行更多操作,例如每次属性更改时的回调,与Zookeeper或其他服务的集成。您可以在GitHub上找到本文中显示的所有代码。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,360评论 6 343
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 昨天园长特意给我们老师开会,表情十分严肃地特别强调老师的师德。 作为一名幼儿园老师只能陪孩子度过幼儿园的美好时光,...
    清清的海阅读 1,097评论 14 18
  • 2018年2月9号 天气晴 今天和往常一样早上起来给儿子做好饭,吃了去送他学思维导图,老师说今天上午最后一节课,下...
    和儿子一起成长阅读 313评论 0 2