Javaアプリケーション、特にSpringサービスの構成を配布するために使用されているApache ZooKeeperの十分に文書化されたユースケースはありますか?
クラウドサービスの多くのユーザーと同様に、可変量の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;
}
}
}
私は先週、JamesStrachenからのApacheCamelの講演に参加しました。彼は、構成情報のソースとして、ZooKeeperを内部で使用することについて言及しました クラウド内のJavaベースのサーバー 。
Springでのランタイム構成の変更についてAdrianColyer(SpringSourceのCTO)からの話を見ましたが、Springは今日これをサポートしていますか?
私の意見では、典型的に設計されたSpringアプリケーションから始めている場合、動的な構成変更をその上に後付けする簡単な仕事をしているとは思いません。
Spring CloudConfigを検討する必要があります。
http://projects.spring.io/spring-cloud/
Spring Cloud Configgitリポジトリに裏打ちされた一元化された外部構成管理。構成リソースはSpring
Environment
に直接マップされますが、必要に応じてSpring以外のアプリケーションで使用できます。
ここで入手可能なソースコード:
https://github.com/spring-cloud/spring-cloud-config
ここのサンプルアプリケーション:
propertyplaceholderconfigurer
でgithub
としてSpringBean統合ZookeeperとSpringFrameworkのセットを作成しました: https://github.com/james-wu-shanghai/spring-zookeeper.git ご覧いただけます。
特に春ではありませんが、Java一般的に、ZooKeeperを検出サーバーとして使用して更新されたバンドルをコンテナーにプッシュする分散OSGI標準のCXF実装があります: http: //cxf.Apache.org/dosgi-discovery.html 。
Zookeeperは、分散アプリケーションの構成管理にCurator APIを使用して、より高度な抽象化で非常にうまく活用できます。開始するには、次の2つの手順に従ってください。
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")));
}
}
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を作成します。
<bean id="zkProperties" class="fms.ZkPropertiesFactoryBean" p:zkConnect="localhost:2181" p:path="/app/zk-properties"/>
<context:property-placeholder properties-ref="zkProperties"/>