Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ZooKeeper for Java/Spring Config?

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

    }

}
like image 557
EngineerBetter_DJ Avatar asked Mar 30 '12 09:03

EngineerBetter_DJ


People also ask

How do I use ZooKeeper for configuration management?

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.

What is essential configuration for ZooKeeper?

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.


2 Answers

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

like image 191
btiernay Avatar answered Oct 05 '22 23:10

btiernay


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.

like image 41
james_wu_shanghai Avatar answered Oct 06 '22 00:10

james_wu_shanghai