使用Zookeeper存储Spring中properties的统一配置 - Go语言中文社区

使用Zookeeper存储Spring中properties的统一配置


把分布式项目的比如数据库访问地址,用户名等,统一放到Zookeeper中进行管理,项目只需指定zookeeper地址即可。

一、先添加maven依赖:

<!-- ZooKeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.4.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.4.2</version>
<scope>provided</scope>
</dependency>
<!-- Zookeeper -->

然后只需两个类即可完成目标:

二、类ZooKeeperService.java

package cn.com.easy.zookeeper;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.collections.CollectionUtils;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

/**
 * zookeeper服务类,用于创建操作zookeeper的对象
 * 
 * @author nibili 2015年5月7日
 * 
 */
public class ZooKeeperService {

	private Logger logger = LoggerFactory.getLogger(ZooKeeperService.class);
	public static final int MAX_RETRIES = 3000;
	public static final int BASE_SLEEP_TIMEMS = 3000;
	/** zookeeper服务器列表 */
	private String zookeeperServers = "";
	/** zookeeper客户端操纵对象 */
	private CuratorFramework client;
	/** 监听器集合(一键多值数据结构) */
	private Multimap<IZookeeperWatch, Object> watchesMap = ArrayListMultimap.create();

	public ZooKeeperService(String zookeeperServers) {
		this.zookeeperServers = zookeeperServers;
		RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIMEMS, MAX_RETRIES);
		this.client = CuratorFrameworkFactory.builder().connectString(this.zookeeperServers).retryPolicy(retryPolicy).build();
		client.start();
	}

	/**
	 * 取消监听,
	 * 
	 * @param zookeeperWatch
	 *            注册监听时的对象
	 * @auth nibili 2015年5月8日
	 */
	public void removeNodeWatch(IZookeeperWatch zookeeperWatch) {
		if (zookeeperWatch == null) {
			logger.info("称除节点监听,监听器对象不能为空!");
			return;
		}
		Collection<Object> values = watchesMap.get(zookeeperWatch);
		if (CollectionUtils.isNotEmpty(values) == true) {
			// 移除监听器
			NodeCache cache = null;
			NodeCacheListener nodeCacheListener = null;
			Iterator<Object> it = values.iterator();
			for (int i = 0; it.hasNext() && i < 2; i++) {
				if (i == 0) {
					cache = (NodeCache) it.next();
				} else if (i == 1) {
					nodeCacheListener = (NodeCacheListener) it.next();
				} else {
					break;
				}
			}
			if (cache != null && nodeCacheListener != null) {
				cache.getListenable().removeListener(nodeCacheListener);
			}

		} else {
			logger.info("没有找到对应的监听器!");
			return;
		}
	}

	/**
	 * 监听节点变化
	 * 
	 * @param zookeeperWatch
	 * @throws Exception
	 * @auth nibili 2015年5月8日
	 */
	public void addNodeWatch(final IZookeeperWatch zookeeperWatch) throws Exception {
		// 是否是每一次触发
		final AtomicBoolean isFirst = new AtomicBoolean(true);
		final NodeCache cache = new NodeCache(this.client, zookeeperWatch.getWatchPath());
		cache.start();
		NodeCacheListener nodeCacheListener = new NodeCacheListener() {

			@Override
			public void nodeChanged() throws Exception {
				// 节点数据
				String data = new String(cache.getCurrentData().getData(), "UTF-8");
				if (isFirst.get() == true) {
					isFirst.set(false);
					logger.debug("NodeCache loaded, data is: " + data);
					zookeeperWatch.handLoad(data);
				} else {
					logger.debug("NodeCache changed, data is: " + data);
					zookeeperWatch.handChange(data);
				}

			}
		};
		cache.getListenable().addListener(nodeCacheListener);
		watchesMap.put(zookeeperWatch, cache);
		watchesMap.put(zookeeperWatch, nodeCacheListener);
	}

	/**
	 * 断开连接
	 * 
	 * @auth nibili 2015年5月7日
	 */
	public void close() {
		client.close();
	}

	/**
	 * 获取zookeeper操纵对象
	 * 
	 * @param servers
	 * @return
	 * @auth nibili 2015年5月7日
	 */
	public CuratorFramework getClient() {
		return client;
	}

	/**
	 * 获取服务器地址
	 * 
	 * @return
	 * @throws Exception
	 * @auth nibili 2015年5月7日
	 */
	public String getServers() {
		return this.zookeeperServers;
	}

	/**
	 * 设置节点值
	 * 
	 * @param path
	 * @param data
	 * @auth nibili 2015年5月8日
	 */
	public void setPathValue(String path, String data) {
		try {
			logger.debug("设置结点值,path:" + path + ",data:" + data);
			this.client.setData().forPath(path, data.getBytes("UTF-8"));
		} catch (Exception e) {
			logger.error("设置zookeeper节点值异常,path:" + path + ",data" + data, e);
		}
	}

	/**
	 * 获取节点值
	 * 
	 * @param path
	 * @return
	 * @throws Exception
	 * @auth nibili 2015年5月7日
	 */
	public byte[] getPathValue(String path) throws Exception {
		if (!exists(this.client, path)) {
			throw new RuntimeException("Path " + path + " does not exists.");
		}
		return client.getData().forPath(path);
	}

	/**
	 * 节点是否存在
	 * 
	 * @param client
	 * @param path
	 * @return
	 * @throws Exception
	 * @auth nibili 2015年5月7日
	 */
	private boolean exists(CuratorFramework client, String path) throws Exception {
		Stat stat = client.checkExists().forPath(path);
		return !(stat == null);
	}

	/**
	 * 获取子节点
	 * 
	 * @param path
	 * @return
	 * @throws Exception
	 * @auth nibili 2015年5月7日
	 */
	public List<String> getSubPaths(String path) throws Exception {
		return client.getChildren().forPath(path);
	}

}

三、类ZooKeeperPropertyPlaceholderConfigurer.java

package cn.com.easy.zookeeper;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import com.google.common.collect.Maps;
/**
 * zookeeper配置<br>
 * 继承spring加载上下文属性文件的类<br>
 * 如果properties中的属性名与zookeeper中的一样,那么参数值将会被zookeeper上的值覆盖。<br>
 * properties文件配置两个参数:<br>
 * zk.servers=192.168.1.156:2181,192.168.1.120:2181 <br>
 * #zk.config.root.path defaut value id "/cn/com/easy/config",u could delete the
 * set<br>
 * #可选,默认为/cn/com/easy/config<br>
 * zk.config.root.path=/cn/com/easy/config<br>
 * 
 * 
 * @author nibili 2015年5月7日
 * 
 */
public class ZooKeeperPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private Logger logger = LoggerFactory.getLogger(ZooKeeperPropertyPlaceholderConfigurer.class);
/** zookeeper服务器地址 的properties参数名,在properties文件中设置 */
private final String ZOOKEEPER_SERVERS_PRO = "zk.servers";
/** 所有配置所在zookeeper的根节点的 的properties参数名,在properties文件中设置 */
private final String ZOOKEEPER_CONFIG_ROOT_PATH_PRO = "zk.config.root.path";
/** 项目配置数据的根节点 */
private final String CONFIG_ROOT_PATH = "/cn/com/easy/config";
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
super.processProperties(beanFactoryToProcess, props);
try {
// zookeeper服务器
String zookeeperServers = props.getProperty(ZOOKEEPER_SERVERS_PRO);
// 配置的根节点
String configRootPath = props.getProperty(ZOOKEEPER_CONFIG_ROOT_PATH_PRO);
if (StringUtils.isBlank(configRootPath) == true) {
configRootPath = CONFIG_ROOT_PATH;
}
Map<String, String> customProperties = this.getConfigurationInZookeeper(zookeeperServers, configRootPath);
props.putAll(customProperties);
logger.debug(props.toString());
} catch (Exception e) {
logger.error("从Zookeeper获取配置异常!" + e.getMessage(), e);
}
}
/**
 * 获取zookeeper中的配置数据
 * 
 * @param zookeeperServers
 * @param configRootPath
 * @return
 * @throws Exception
 * @auth nibili 2015年5月7日
 */
private Map<String, String> getConfigurationInZookeeper(String zookeeperServers, String configRootPath) throws Exception {
// 服务器地址不能为空
if (StringUtils.isBlank(zookeeperServers) == true) {
throw new Exception("Zookeeper服务器地址不能为空!");
}
// 属性名,属性值对应的map
Map<String, String> propertiesInZkMap = Maps.newHashMap();
//
ZooKeeperService zooKeeperService = new ZooKeeperService(zookeeperServers);
// 获取所有子节点
List<String> paths = zooKeeperService.getSubPaths(configRootPath);
// 遍历所有子节点,以及节点值
if (CollectionUtils.isNotEmpty(paths) == true) {
// 遍历有子节点
for (String path : paths) {
byte[] data = zooKeeperService.getPathValue(configRootPath + "/" + path);
if (data != null) {
String value = new String(data, "UTF-8");
if (StringUtils.isNotBlank(value) == true) {
propertiesInZkMap.put(path, value);
}
}
}
}
zooKeeperService.close();
return propertiesInZkMap;
}
}

四、修改properties文件

zk.servers=192.168.1.156:2181,192.168.1.120:2181
#zk.config.root.path defaut value id "/cn/com/easy/config",u could delete the set
zk.config.root.path=/summall/conf

    当然文件中可添加,自己项目要使用的一些属性,也可以生交效,但是如果zookeeper中存在某个属性,那么会被zookeeper中存储的值所替换。

五、修改applicationContext.xml文件

<bean class="cn.com.easy.zookeeper.ZooKeeperPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:applicationContext-zookeeper-config-demo.properties</value>
</list>
</property>
</bean>

六、使用zookeeper客户端添加节点,例:

./zkCli.sh 
[zk: localhost:2181(CONNECTED) 0] create /cn ""
Created /cn
[zk: localhost:2181(CONNECTED) 1] create /cn/com ""
Created /cn/com
[zk: localhost:2181(CONNECTED) 4] create /cn/com/easy ""
Created /cn/com/easy
[zk: localhost:2181(CONNECTED) 5] create /cn/com/easy/config ""
Created /cn/com/easy/config
[zk: localhost:2181(CONNECTED) 15] create /cn/com/easy/config/jdbc.url "jdbc:mysql://localhost:3306/wac"
Created /cn/com/easy/config/jdbc.url
[zk: localhost:2181(CONNECTED) 16] create /cn/com/easy/config/jdbc.username "root"                           
Created /cn/com/easy/config/jdbc.username
[zk: localhost:2181(CONNECTED) 17] create /cn/com/easy/config/jdbc.password "123456"
Created /cn/com/easy/config/jdbc.password


这样配置就完成了,已经可以把zookeeper中的统一配置项加载到spring上下文了!

转载于:https://my.oschina.net/u/1045177/blog/412037

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_33906657/article/details/92078261
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-01 22:08:06
  • 阅读 ( 1156 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢