本文主要完成的目标是:Spring PropertyPlaceholderConfigurer配置文件加载器集成ZooKeeper来实现远程配置读取。
配置管理(Configuration Management)。OK,Let's go!
ZooKeeperPropertyPlaceholderConfigurer.java
继承org.springframework.beans.factory.config.PropertyPlaceholderConfigurer,重写processProperties(beanFactoryToProcess, props)来完成远端配置加载的实现
package org.bigmouth.common.zookeeper.config.spring; import java.io.UnsupportedEncodingException; import java.util.Properties; import org.apache.commons.lang.StringUtils; import org.bigmouth.common.zookeeper.config.Config; import org.bigmouth.common.zookeeper.config.ZooKeeperConfig; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; public class ZooKeeperPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { public static final String PATH = "zoo.paths"; @Override protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException { super.processProperties(beanFactoryToProcess, props); try { fillCustomProperties(props); System.out.println(props); } catch (Exception e) { // Ignore e.printStackTrace(); } } private void fillCustomProperties(Properties props) throws Exception { byte[] data = getData(props); fillProperties(props, data); } private void fillProperties(Properties props, byte[] data) throws UnsupportedEncodingException { String cfg = new String(data, "UTF-8"); if (StringUtils.isNotBlank(cfg)) { // 完整的应该还需要处理:多条配置、value中包含=、忽略#号开头 String[] cfgItem = StringUtils.split(cfg, "="); props.put(cfgItem[0], cfgItem[1]); } } private byte[] getData(Properties props) throws Exception { String path = props.getProperty(PATH); Config config = new ZooKeeperConfig(); return config.getConfig(path); } }
Config.java
配置操作接口package org.bigmouth.common.zookeeper.config; public interface Config { byte[] getConfig(String path) throws Exception; }
程序启动入口
package org.bigmouth.common.zookeeper.config; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Startup { public static void main(String[] args) { new ClassPathXmlApplicationContext("classpath:/config/applicationContext.xml"); } }
配置操作接口ZooKeeper的实现
package org.bigmouth.common.zookeeper.config; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.data.Stat; public class ZooKeeperConfig implements Config { @Override public byte[] getConfig(String path) throws Exception { CuratorFramework client = ZooKeeperFactory.get(); if (!exists(client, path)) { throw new RuntimeException("Path " + path + " does not exists."); } return client.getData().forPath(path); } private boolean exists(CuratorFramework client, String path) throws Exception { Stat stat = client.checkExists().forPath(path); return !(stat == null); } }
管理ZooKeeper客户端连接
package org.bigmouth.common.zookeeper.config; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; public class ZooKeeperFactory { public static final String CONNECT_STRING = "172.16.3.42:2181,172.16.3.65:2181,172.16.3.24:2181"; public static final int MAX_RETRIES = 3; public static final int BASE_SLEEP_TIMEMS = 3000; public static final String NAME_SPACE = "cfg"; public static CuratorFramework get() { RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIMEMS, MAX_RETRIES); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(CONNECT_STRING) .retryPolicy(retryPolicy) .namespace(NAME_SPACE) .build(); client.start(); return client; } }
applicationContext.xml
配置加载器使用我们自己创建的ZooKeeperPropertyPlaceholderConfigurer,因为它重写了processProperties方法。这个方法里会去读取远程配置。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean class="org.bigmouth.common.zookeeper.config.spring.ZooKeeperPropertyPlaceholderConfigurer"> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> <property name="ignoreResourceNotFound" value="true" /> <property name="locations"> <list> <value>classpath:application.properties</value> </list> </property> </bean> </beans>
application.properties
项目配置文件,里面除了配置ZooKeeper服务器地址和读取的节点以外,其他所有的配置都应该保存在ZooKeeper中。
登录ZooKeeper中为节点 /cfg/properties 添加一条配置项:
如图所示:我创建了一个节点 /cfg/properties 并设置内容为:jdbc.driver=org.postgresql.Driver
OK 了,zoo.paths是本地application.properties文件中的,jdbc.driver是远程ZooKeeper服务器中的。
<dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>3.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>3.0.3.RELEASE</version> </dependency> <!-- ZooKeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.4.2</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.4.2</version> </dependency>
相关资料:
Apache Curator FrameworkApache ZooKeeper
博主,问个zk值被刷了的问题。
假设,我们现在配置的数据,都是从zk中获取的,但是某个值被刷新了,我们有个watch监控到了,但是这个值怎么刷入到内存中,或者说重新注册那个bean?
有没有这方面的解决思路,我有点迷惑
ZKClient 可以轻松监听到配置什么时候变化,但变化后该怎么做?