zookeeper集群实现配置中心
引言
上篇写了搭建zookeeper集群的笔记,此篇就用此集群作为配置中心,使用zookeeper的api,实现配置的热加载。
实验场景
起一个服务,动态加载zookeeper中的配置,根据配置的Class路径,反射获得不同的对象,执行不同的方法。
具体步骤
一、添加zookeeper依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
二、封装zookeeper对象获取工具类
该类封装了zookeeper对象的获得过程,可以直接通过其获得zookeeper对象。
public class ZKUtil {
private static ZooKeeper zk;
private static String urls = "192.168.128.128:2181,192.168.128.132:2181" +
",192.168.128.130:2181,192.168.128.131:2181/testLock";
private DefaltWatch watcher = new DefaltWatch();
private CountDownLatch countDownLatch = new CountDownLatch(1);
public ZooKeeper getZk() {
try {
zk = new ZooKeeper(urls,1000,watcher);
watcher.setCc(countDownLatch);
countDownLatch.await();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
// 加个锁防止zk还没连接到
return zk;
}
}
三、自定义监听类
该类充当监听器,这里主要用于实例化zookeeper时,监听实例化过程。为了保证成功建立连接后才返回对象。
public class DefaultWatch implements Watcher {
CountDownLatch cc;
public void setCc(CountDownLatch cc) {
this.cc = cc;
}
@Override
public void process(WatchedEvent watchedEvent) {
Event.KeeperState state = watchedEvent.getState();
switch (state) {
case Unknown:
break;
case Disconnected:
break;
case NoSyncConnected:
break;
case SyncConnected:
System.out.println("zookeeper connectioned");
cc.countDown();
break;
case AuthFailed:
break;
case ConnectedReadOnly:
break;
case SaslAuthenticated:
break;
case Expired:
break;
}
}
}
四、自定义配置定义类
其属性对应zookeeper中配置的信息。
public class MyConfig {
private String className;
}
五、封装回调监听类
该类是zookeeper的核心类,使用zookeeper watch的功能,配合回调,zookeeper中配置文件更新时,动态加载配置信息。
public class WatchCallback implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
ZooKeeper zk;
MyConfig config;
CountDownLatch cc = new CountDownLatch(1);
public void setZk(ZooKeeper zk) {
this.zk = zk;
}
public void setConfig(MyConfig config) {
this.config = config;
}
/**
* 回调getData, 看bytes数组
* @param i
* @param s
* @param o
* @param bytes
* @param stat
*/
@Override
public void processResult(int i, String s, Object o, byte[] bytes, Stat stat) {
if(bytes != null ){
config.setClassName(new String(bytes));
cc.countDown();
}
}
/**
* 回调exists
* @param i
* @param s
* @param o
* @param stat
*/
@Override
public void processResult(int i, String s, Object o, Stat stat) {
if(stat != null) {
System.out.println("exist");
zk.getData("/testConfig", this, this, "aaa");
}
}
/**
* 第一次是监听exist的
* 监听getData
* @param watchedEvent
*/
@Override
public void process(WatchedEvent watchedEvent) {
Event.EventType type = watchedEvent.getType();
switch (type) {
case None:
break;
case NodeCreated:
zk.getData("/testConfig",this,this,"aaa");
break;
case NodeDeleted:
config.setClassName("");
cc = new CountDownLatch(1);
break;
case NodeDataChanged:
zk.getData("/testConfig",this,this,"aaa");
// cc.countDown();
break;
case NodeChildrenChanged:
break;
}
}
// 判断是否存在,设置监听器和回调函数
public void await() {
// 回调后监听交给getData了
zk.exists("/testConfig",this,this,"ccc");
try {
cc.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
六、创建接口和实例
接口
public interface ObjectParent {
String sayHello();
}
实现类一
public class Object1 implements ObjectParent{
@Override
public String sayHello() {
return "hello Object1";
}
}
实现类二
public class Object2 implements ObjectParent {
@Override
public String sayHello() {
return "hello Object2";
}
}
七、编写main类实现
以下代码通过zookeeper配置类的名称,通过反射获得对应类的对象,调用对应的方法。
public static void main(String[] args) {
WatchCallback watchCallback = new WatchCallback();
ZookeeperUtil zkUtil = new ZookeeperUtil();
ZooKeeper zk = zkUtil.getZookeeper();
MyConfig conf = new MyConfig();
watchCallback.setConfig(conf);
watchCallback.setZk(zk);
watchCallback.await();
ObjectParent objectParent;
try {
while(true) {
Class cl = Class.forName(conf.getClassName());
objectParent = (ObjectParent) cl.newInstance();
System.out.println(objectParent.sayHello());
}
}catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException classNotFoundException) {
classNotFoundException.printStackTrace();
}
}
实验结果
zookeeper输入create /testConfig/testConfig com.test.zookeeper.Object1
输出 hello Object1
zookeeper输入create /testConfig/testConfig com.test.zookeeper.Object1
输出 hello Object2
写在最后
本篇通过,小例子实现zookeeper做为配置中心的玩法,可以自行研究,解锁更多用法。下篇将用zookeeper实现分布式锁。