Dynamic Configuration Management with Netflix Archaius and Apache ZooKeeper, Property-Files, JMX

April 13th, 2016 by

Though 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.

Updating Archaius settings with JMX and JConsole

Updating Archaius settings with JMX and JConsole

 

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?

  1. We’re starting a new ZooKeeper instance for demonstration purpose
  2. We’re initializing the ZooKeeper client and we’re setting a value ‘http://www.test.de for the znode path /config/search.url
  3. We’re setting a system property that allows us to modify configuration values using JMX and the Netflix MBeans
  4. We’re initializing different configuration sources: a source for our property file, a source for reading system properties, a source for ZooKeeper
  5. 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!
  6. 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
  7. We’re now printing the current value of this property which should be http://www.test.de
  8. 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:

Updating Archaius settings with JMX and JConsole

Updating Archaius settings with JMX and JConsole

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: , , , , , , , , , , , , , ,

One Response to “Dynamic Configuration Management with Netflix Archaius and Apache ZooKeeper, Property-Files, JMX”

  1. Aman Says:

    [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

Search
Categories