ZooKeeper pour Java / config de printemps?
y a-t-il des cas bien documentés D'utilisation D'Apache Zokeeper pour distribuer la configuration des applications Java, et en particulier les services Spring?
<!-Comme beaucoup d'utilisateurs de services cloud, j'ai l'obligation de modifier la configuration D'une quantité variable de services Java, de préférence à l'exécution, sans avoir à redémarrer les services.UPDATE
finalement j'ai fini par écrire quelque chose qui chargerait un noeud de gardien de Zoo comme un fichier de propriétés, et de créer un ResourcePropertySource
et l'insérer dans un Printemps contexte. Notez que cela ne reflétera pas les changements dans le noeud de ZooKeeper après le début du contexte.
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 réponses
j'étais à un Apache Camel talk de James Strachen la semaine dernière et il a mentionné utiliser ZooKeeper sous les couvertures pour leur serveur basé sur Java dans le cloud comme source d'informations de configuration.
J'ai vu un discours D'Adrian Colyer (CTO de SpringSource) sur le changement de configuration de runtime au printemps, mais est-ce que Spring supporte cela aujourd'hui?
à mon avis, si vous êtes à partir d'une application de ressort typiquement architectonnée, Je ne vous vois pas avoir un travail facile modification de la configuration dynamique.
Vous devriez considérer la Config de nuage de printemps:
http://projects.spring.io/spring-cloud/
configuration du nuage de printemps gestion centralisée de la configuration externe soutenu par un dépôt git. La carte des ressources de configuration directement au Printemps
Environment
mais pourrait être utilisé par des applications sans ressort si vous le souhaitez.
code Source disponible ici:
https://github.com/spring-cloud/spring-cloud-config
Exemple d'application ici:
Pas de printemps en particulier, mais pour java, il existe généralement un CXF mise en œuvre de la distribué OSGI standard qui utilise ZooKeeper que le serveur de découverte de pousser les mises à jour des faisceaux vers le conteneur : http://cxf.apache.org/dosgi-discovery.html.
j'ai créé un ensemble de haricots de printemps intégration zookeeper et springfram framework comme propertyplaceholderconfigurer
, en github
: https://github.com/james-wu-shanghai/spring-zookeeper.git
vous pouvez prendre un coup d'oeil.
Zookeeper peut être très bien exploité avec une abstraction plus élevée en utilisant les API de Curator pour la gestion de la configuration dans les applications distribuées. Pour commencer il suffit de suivre ces deux étapes.
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
pour récupérer ces propriétés dans le programme java, référez ce code snippet.
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")));
}
}
après avoir trouvé une suggestion d'utiliser un FactoryBean
pour remplir un PropertyPlaceholderConfigurer
j'ai fait ceci:
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;}
}
Dans le spring-config.xml
vous créez les haricots comme suit:
<bean id="zkProperties" class="fms.ZkPropertiesFactoryBean" p:zkConnect="localhost:2181" p:path="/app/zk-properties"/>
<context:property-placeholder properties-ref="zkProperties"/>