Dynamic Configuration Management with Netflix Archaius and Apache ZooKeeper, Property-Files, JMX
April 13th, 2016 by Micha KopsThough having written about other configuration management libraries for Java before, I would like to demonstrate another one today: Netflix Archaius.
Archaius offers some nice features like dynamic typed properties, thread-safe operations, an event system for property changes/updates, a JMX MBean to read and update properties and adaptors for a variety of dynamic configuration sources like Amazon DynamoDB, JDBC, URLs and Apache ZooKeeper.
In the following tutorial I’d like to demonstrate how to read and update application configuration properties with Archaius and data sources like property-files, system-properties, JMX and Apache ZooKeeper.
Contents
Dependencies
Using Maven we’re adding the following three dependencies to our project’s pom.xml:
- archaius-core: The Archaius library
- archaius-zookeeper: Apache ZooKeeper support for Archaius
- curator-test: Lets us create ZooKeeper instances for test environments with ease
<dependencies> <dependency> <groupId>com.netflix.archaius</groupId> <artifactId>archaius-core</artifactId> <version>0.7.4</version> </dependency> <dependency> <groupId>com.netflix.archaius</groupId> <artifactId>archaius-zookeeper</artifactId> <version>0.7.4</version> </dependency> <dependency> <groupId>com.netflix.curator</groupId> <artifactId>curator-test</artifactId> <version>1.3.3</version> </dependency> </dependencies>
Sample Application
As we’re going to demonstrate reading configuration files from different sources, we’re adding the following configuration file to our project (src/main/resources):
config.properties
search.url=https://duckduckgo.com/?q=
And this is our application in a single class.
What is happening here?
- We’re starting a new ZooKeeper instance for demonstration purpose
- We’re initializing the ZooKeeper client and we’re setting a value ‘http://www.test.de‘ for the znode path /config/search.url
- We’re setting a system property that allows us to modify configuration values using JMX and the Netflix MBeans
- We’re initializing different configuration sources: a source for our property file, a source for reading system properties, a source for ZooKeeper
- We’re adding all these configuration sources to a combined configuration, our final configuration. Please note, that the order of the configuration sources is important as it defines the way, we’re searching for specific configuration values from the sources defined!
- Now we’re creating a new dynamic property for the key search.url and we’re adding a callback that prints the new value of this property when it has changed
- We’re now printing the current value of this property which should be http://www.test.de
- Then we’re updating the value in our ZooKeeper instance using the test client and the callback should print that value (http://www.foo.de)
package com.hascode.tutorial; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.configuration.SystemConfiguration; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryOneTime; import com.google.common.io.Files; import com.netflix.config.ConcurrentCompositeConfiguration; import com.netflix.config.ConcurrentMapConfiguration; import com.netflix.config.ConfigurationManager; import com.netflix.config.DynamicPropertyFactory; import com.netflix.config.DynamicStringProperty; import com.netflix.config.DynamicWatchedConfiguration; import com.netflix.config.source.ZooKeeperConfigurationSource; import com.netflix.curator.test.TestingServer; public class App { public static void main(String[] args) throws Exception { try (TestingServer zkTestServer = new TestingServer(2181, Files.createTempDir()); CuratorFramework testClient = CuratorFrameworkFactory.newClient(zkTestServer.getConnectString(), new RetryOneTime(2000))) { testClient.start(); testClient.create().forPath("/config"); testClient.create().forPath("/config/search.url", "http://www.test.de".getBytes()); // enable JMX dynamic configuration System.setProperty(DynamicPropertyFactory.ENABLE_JMX, "true"); // enable config from properties file ConcurrentMapConfiguration configFile = new ConcurrentMapConfiguration( new PropertiesConfiguration("config.properties")); // enable configuration from system properties ConcurrentMapConfiguration systemProps = new ConcurrentMapConfiguration(new SystemConfiguration()); CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", new ExponentialBackoffRetry(1000, 3)); client.start(); ZooKeeperConfigurationSource zkConfigSource = new ZooKeeperConfigurationSource(client, "/config"); zkConfigSource.start(); System.out.println("value in zookeeper: " + zkConfigSource.getCurrentData().get("search.url")); DynamicWatchedConfiguration zkDynamicConfig = new DynamicWatchedConfiguration(zkConfigSource); // composite configuration ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration(); config.addConfiguration(zkDynamicConfig); config.addConfiguration(configFile); config.addConfiguration(systemProps); ConfigurationManager.install(config); DynamicStringProperty prop = DynamicPropertyFactory.getInstance().getStringProperty("search.url", "https://www.google.com/#q="); prop.addCallback(() -> System.out.println("search url has changed to " + prop.get())); System.out.println("search url is: " + prop.get()); testClient.setData().forPath("/config/search.url", "http://www.foo.de".getBytes()); System.console().readLine("press enter to shutdown application\n"); System.out.println("shutting down application.."); client.close(); } } }
Running the Example
We’re now ready to run our example application in our IDE of choice or using Maven in the command line like this:
$ mvn exec:java -Dexec.mainClass=com.hascode.tutorial.App [INFO] ------------------------------------------------------------------------ [INFO] Building netflix-archaius-tutorial 1.0.0 [INFO] ------------------------------------------------------------------------ value in zookeeper: http://www.test.de search url is: http://www.test.de press enter to shutdown application search url has changed to http://www.foo.de shutting down application..
To update the configuration value we may simply use JConsole, connect to our process and update the value as show on the following screenshot:
Tutorial Sources
Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:
git clone https://bitbucket.org/hascode/netflix-archaius-tutorial.git
Resources
Alternative: Java Config-Builder
I have written about another configuration library in my tutorial “Using Java Config-Builder to assemble your Application Configuration“, please feel free to read.
Netflix Hystrix
If interested in using Netflix Hystrix, please feel free to read the following tutorial of mine: “Resilient Architecture in Practice – Circuit Breakers for Java: Failsafe, Javaslang, Hystrix and Vert.x“.
Article Updates
- 2017-02-14: Link to circuit-breaker article added.
Tags: archaius, config, configuration, curator, jconsole, jdbc, jmx, mbean, netflix, properties, property, reactive, resilient, settings, zookeeper
January 19th, 2017 at 8:30 pm
[INFO] BUILD FAILURE
[INFO] ————————————————————————
[ERROR] [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.5.1:compile (default-compile) on project netflix-archaius-tutorial: Compilation failure: Compilation failure:
[ERROR] /C:/cactus/docker-apps/Archaius/src/main/java/com/hascode/tutorial/App.java:[3,40] package org.apache.commons.configuration does not exist
[ERROR] /C:/cactus/docker-apps/Archaius/src/main/java/com/hascode/tutorial/App.java:[4,40] package org.apache.commons.configuration does not exist
[ERROR] /C:/cactus/docker-apps/Archaius/src/main/java/com/hascode/tutorial/App.java:[5,36] package org.apache.curator.framework does not exist
[ERROR] /C:/cactus/docker-apps/Archaius/src/main/java/com/hascode/tutorial/App.java:[6,36] package org.apache.curator.framework does not exist
[ERROR] /C:/cactus/docker-apps/Archaius/src/main/java/com/hascode/tutorial/App.java:[7,32] package org.apache.curator.retry does not exist
[ERROR] /C:/cactus/docker-apps/Archaius/src/main/java/com/hascode/tutorial/App.java:[8,32] package org.apache.curator.retry does not exist