当前位置: 首页 > news >正文

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

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

把分布式项目的比如数据库访问地址,用户名等,统一放到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

相关文章:

  • 第四十二条:慎用可变参数
  • java使用Iterator、for循环同步数据
  • 不要盲目迷信多线程
  • 磁盘的5种卷,RAID—5的修复
  • IOS学习笔记--Objective-C之KVC、KVO
  • Skype for Business实战演练之八:安装Skype for Business Server 2015
  • android intent 传数据
  • java中HashSet详解(转)
  • 当我完善几年前的一个老项目时,我做了哪些改进
  • 简述ASP.NET MVC原理
  • 代码中的良好习惯从点滴做起
  • linux 下配置文件目录/etc/sysconfig
  • RabbitMQ与Redis做队列比较
  • HttpClient模拟客户端请求实例
  • SQL Server 字段类型 decimal(18,6)小数点前是几位?记一次数据库SP的BUG处理
  • JavaScript-如何实现克隆(clone)函数
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 10个确保微服务与容器安全的最佳实践
  • CEF与代理
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • Git的一些常用操作
  • go append函数以及写入
  • input实现文字超出省略号功能
  • Java应用性能调优
  • Js基础——数据类型之Null和Undefined
  • Laravel Mix运行时关于es2015报错解决方案
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • React as a UI Runtime(五、列表)
  • SpingCloudBus整合RabbitMQ
  • Vue ES6 Jade Scss Webpack Gulp
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 对象引论
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 精彩代码 vue.js
  • 聊聊flink的TableFactory
  • 配置 PM2 实现代码自动发布
  • 前端相关框架总和
  • 微信公众号开发小记——5.python微信红包
  • 在Mac OS X上安装 Ruby运行环境
  • 白色的风信子
  • puppet连载22:define用法
  • ​油烟净化器电源安全,保障健康餐饮生活
  • #HarmonyOS:基础语法
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (蓝桥杯每日一题)love
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (十六)Flask之蓝图
  • (译) 函数式 JS #1:简介
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载