Are there any well documented use cases of Apache ZooKeeper being used to distribute configuration of Java applications, and in particular Spring services?
Like many users of cloud services I have a requirement to change the configuration of a variable amount of Java services, preferably at run-time without needing to restart the services.
UPDATE
Eventually I ended up writing something that would load a ZooKeeper node as a properties file, and create a ResourcePropertySource
and insert it into a Spring context. Note that this will not reflect changes in the ZooKeeper node after the context has started.
public class ZooKeeperPropertiesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger logger = LoggerFactory.getLogger(ZooKeeperPropertiesApplicationContextInitializer.class);
private final CuratorFramework curator;
private String projectName;
private String projectVersion;
public ZooKeeperPropertiesApplicationContextInitializer() throws IOException {
logger.trace("Attempting to construct CuratorFramework instance");
RetryPolicy retryPolicy = new ExponentialBackoffRetry(10, 100);
curator = CuratorFrameworkFactory.newClient("zookeeper", retryPolicy);
curator.start();
}
/**
* Add a primary property source to the application context, populated from
* a pre-existing ZooKeeper node.
*/
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
logger.trace("Attempting to add ZooKeeper-derived properties to ApplicationContext PropertySources");
try {
populateProjectProperties();
Properties properties = populatePropertiesFromZooKeeper();
PropertiesPropertySource propertySource = new PropertiesPropertySource("zookeeper", properties);
applicationContext.getEnvironment().getPropertySources().addFirst(propertySource);
logger.debug("Added ZooKeeper-derived properties to ApplicationContext PropertySources");
curator.close();
} catch (IOException e) {
logger.error("IO error attempting to load properties from ZooKeeper", e);
throw new IllegalStateException("Could not load ZooKeeper configuration");
} catch (Exception e) {
logger.error("IO error attempting to load properties from ZooKeeper", e);
throw new IllegalStateException("Could not load ZooKeeper configuration");
} finally {
if (curator != null && curator.isStarted()) {
curator.close();
}
}
}
/**
* Populate the Maven artifact name and version from a property file that
* should be on the classpath, with values entered via Maven filtering.
*
* There is a way of doing these with manifests, but it's a right faff when
* creating shaded uber-jars.
*
* @throws IOException
*/
private void populateProjectProperties() throws IOException {
logger.trace("Attempting to get project name and version from properties file");
try {
ResourcePropertySource projectProps = new ResourcePropertySource("project.properties");
this.projectName = (String) projectProps.getProperty("project.name");
this.projectVersion = (String) projectProps.getProperty("project.version");
} catch (IOException e) {
logger.error("IO error trying to find project name and version, in order to get properties from ZooKeeper");
}
}
/**
* Do the actual loading of properties.
*
* @return
* @throws Exception
* @throws IOException
*/
private Properties populatePropertiesFromZooKeeper() throws Exception, IOException {
logger.debug("Attempting to get properties from ZooKeeper");
try {
byte[] bytes = curator.getData().forPath("/distributed-config/" + projectName + "/" + projectVersion);
InputStream in = new ByteArrayInputStream(bytes);
Properties properties = new Properties();
properties.load(in);
return properties;
} catch (NoNodeException e) {
logger.error("Could not load application configuration from ZooKeeper as no node existed for project [{}]:[{}]", projectName, projectVersion);
throw e;
}
}
}
Clients can connect to the zookeeper server and request for the data by passing the full ZNode path. Clean Structure: ZNodes allow to logically arrange your configurations. eg: Global Config, Application wise config, environment wise config etc.
Required Software. ZooKeeper runs in Java, release 1.6 or greater (JDK 6 or greater). It runs as an ensemble of ZooKeeper servers. Three ZooKeeper servers is the minimum recommended size for an ensemble, and we also recommend that they run on separate machines.
You should consider Spring Cloud Config:
http://projects.spring.io/spring-cloud/
Spring Cloud Config Centralized external configuration management backed by a git repository. The configuration resources map directly to Spring
Environment
but could be used by non-Spring applications if desired.
Source code available here:
https://github.com/spring-cloud/spring-cloud-config
Sample application here:
https://github.com/spring-cloud/spring-cloud-config/blob/master/spring-cloud-config-sample/src/main/java/sample/Application.java
I created a set of spring beans integration zookeeper and springframework as propertyplaceholderconfigurer
, in github
: https://github.com/james-wu-shanghai/spring-zookeeper.git
you can take a look.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With