ZooKeeper для Java / Spring Config? - PullRequest
19 голосов
/ 30 марта 2012

Существуют ли хорошо документированные случаи использования Apache ZooKeeper для распространения конфигурации приложений Java и, в частности, сервисов Spring?

Как и у многих пользователей облачных сервисов, у меня есть требование изменить конфигурацию переменного количества сервисов Java, предпочтительно во время выполнения, без необходимости перезапуска сервисов.

UPDATE

В конце концов я написал что-то, что загружало бы узел ZooKeeper в виде файла свойств, создавал ResourcePropertySource и вставлял его в контекст Spring. Обратите внимание, что это не будет отражать изменения в узле ZooKeeper после запуска контекста.

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

    }

}

Ответы [ 6 ]

6 голосов
/ 14 сентября 2014

Вы должны рассмотреть Spring Cloud Config:

http://projects.spring.io/spring-cloud/

Spring Cloud Config Централизованное управление внешней конфигурацией при поддержке git-репозитория. Конфигурационные ресурсы отображаются напрямую в Spring Environment, но может использоваться приложениями, отличными от Spring при желании.

Исходный код доступен здесь:

https://github.com/spring-cloud/spring-cloud-config

Пример приложения здесь:

https://github.com/spring-cloud/spring-cloud-config/blob/master/spring-cloud-config-sample/src/main/java/sample/Application.java

2 голосов
/ 11 мая 2013

Я создал набор интегрированных zookeeper и springframework для бобовых пружин как propertyplaceholderconfigurer, в github: https://github.com/james-wu-shanghai/spring-zookeeper.git вы можете посмотреть.

1 голос
/ 11 января 2017

Zookeeper может быть очень эффективно использован с более высокой абстракцией с использованием API-интерфейсов Curator для управления конфигурацией в распределенных приложениях.Для начала просто выполните следующие два шага.

STEP 1 : Start zookeper server and then start zookeeper cli and create some znodes. Znodes are nothing but UNIX like files which contain values, and name of files depict property name.
To create/fetch/update properties use these commands on zookeeper cli.

create /system/dev/example/port 9091
get /system/dev/example/port
set /system/dev/example/port 9092

Чтобы получить эти свойства в Java-программе, используйте этот фрагмент кода.

import java.util.HashMap;
import java.util.Map;

import org.apache.curator.framework.CuratorFramework; 
import org.apache.curator.framework.CuratorFrameworkFactory; 
import org.apache.curator.retry.ExponentialBackoffRetry;
public class App 
{
    public static void main( String[] args ) throws Exception
    {
         final String ZK = "localhost:2181"; 

         final Map<String, String> data = new HashMap<String, String>();         
         CuratorFramework client = CuratorFrameworkFactory.newClient(ZK, new ExponentialBackoffRetry(100, 3)); 
         client.start();
         System.out.println(new String(client.getData().forPath("/system/dev/example/port")));
       }  
}
1 голос
/ 30 марта 2012

Не особенно в частности, но для java в целом, существует реализация CXF распределенного стандарта OSGI, который использует ZooKeeper в качестве сервера обнаружения для отправки обновленных пакетов в контейнер: http://cxf.apache.org/dosgi-discovery.html.

0 голосов
/ 16 ноября 2013

Найдя предложение использовать FactoryBean для заполнения обычного PropertyPlaceholderConfigurer, я построил это:

package fms;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Properties;

public class ZkPropertiesFactoryBean extends AbstractFactoryBean<Properties> implements Watcher {
    private Logger LOGGER = LoggerFactory.getLogger(ZkPropertiesFactoryBean.class);
    private String zkConnect;
    private String path;
    private int timeout = 1000;

    @Override protected Properties createInstance() throws Exception {
        long start = System.currentTimeMillis();
        Properties p = new Properties();
        p.load(new ByteArrayInputStream(loadFromZk()));
        double duration = (System.currentTimeMillis() - start)/1000d;
        LOGGER.info(String.format("Loaded %d properties from %s:%s in %2.3f sec", p.size(), zkConnect, path, duration));
        return p;
    }

    @Override public Class<Properties> getObjectType() {
        return Properties.class;
    }

    private byte[] loadFromZk() throws IOException, KeeperException, InterruptedException {Stat stat = new Stat();
        ZooKeeper zk = new ZooKeeper(zkConnect, timeout, this);
        return zk.getData(path, false, stat);
    }

    @Override public void process(WatchedEvent event) {}

    public void setPath(String path) {this.path = path;}

    public void setZkConnect(String zkConnect) {this.zkConnect = zkConnect;}
}

В spring-config.xml вы создаете бины следующим образом:

<bean id="zkProperties" class="fms.ZkPropertiesFactoryBean" p:zkConnect="localhost:2181" p:path="/app/zk-properties"/>
<context:property-placeholder properties-ref="zkProperties"/>
0 голосов
/ 30 марта 2012

Я был на беседе с Apache Camel от Джеймса Страчена на прошлой неделе, и он упомянул об использовании ZooKeeper под прикрытием для их Java-сервера в облаке в качестве источника информации о конфигурации.

Я видел разговор Адриана Колера (Crian of SpringSource) об изменении конфигурации среды выполнения в Spring, но поддерживает ли Spring это сегодня?

По моему мнению, если вы начинаете с обычно спроектированного Spring-приложенияЯ не вижу, чтобы у вас была простая работа по модификации динамических изменений конфигурации поверх этого.

...