1. 需求概述
程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到ZooKeeper上去,保存在 ZooKeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 ZooKeeper 的通知,然后从ZooKeeper 获取新的配置信息应用到系统中就好。
客户端发送消息到zk一次即可!
集群中的所有的服务器,都监听。都能立即受到关于这个消息的通知
2. 实现
package de.apps.config;
import org.apache.zookeeper.*;
import java.util.List;
/**
* Copyright (c) 2019 leyou ALL Rights Reserved Project: learning Package: app.配置管理 Version: 1.0
*
* @author qingzhi.wu
* @date 2020/6/21 11:24
*/
public class DataNode {
private static final String Connect_String = "cdh01:2181,cdh02:2181,cdh03:2181";
private static final int Session_Timeout = 4000;
private static final String PARENT_NODE = "/config";
private static List<String> oldChildNodeList = null;
private static ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
// 1. 获取zk连接
zk =
new ZooKeeper(
Connect_String,
Session_Timeout,
new Watcher() {
public void process(WatchedEvent event) {
// 6.处理监听逻辑
// 6.1 获取监听事件信息
String path = event.getPath();
Event.EventType type = event.getType();
Event.KeeperState state = event.getState();
// 为了屏蔽启动时有空值问题,给他屏蔽掉 也就是 type =None path = null
if (state.getIntValue() == 3 && path != null) {
// 这里处理的意义也就是让他无限监听,不会中断
List<String> newChildrenNodeList = null;
// 获取触发后节点信息
try {
newChildrenNodeList = zk.getChildren(PARENT_NODE, true);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 判断子节点有变更
if (path.equals(PARENT_NODE) && type == Event.EventType.NodeChildrenChanged) {
// 处理添加逻辑
if (oldChildNodeList.size() < newChildrenNodeList.size()) {
// 此时说明有配置新增
// 获取新增
String diffBetweenTwoList =
getDiffBetweenTwoList(oldChildNodeList, newChildrenNodeList);
// 获取数据
byte[] data = null;
try {
data = zk.getData(PARENT_NODE + "/" + diffBetweenTwoList, true, null);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"服务器集群 add 了一项配置: key="
+ diffBetweenTwoList
+ ", value="
+ new String(data));
}
} else {
// 此时处理删除逻辑,判断是否是删除操作,因为其中也包含更新操作
for (String s : oldChildNodeList) {
String childPath = PARENT_NODE + "/" + s;
if (path.equals(childPath) && type == Event.EventType.NodeDeleted) {
// 删除的配置的key
String diffServer =
getDiffBetweenTwoList(newChildrenNodeList, oldChildNodeList);
System.out.println("服务器集群 delete 了一项配置: key=" + diffServer);
}
}
}
// 此时只剩余更新操作
for (String child : oldChildNodeList) {
String childPath = PARENT_NODE + "/" + child;
if (path.equals(childPath) && Event.EventType.NodeDataChanged == type) {
byte[] data = null;
try {
data = zk.getData(childPath, true, null);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
String newValue = new String(data);
System.out.println(
"服务器集群 update 了一项配置: key=" + child + ", newValue=" + newValue);
break;
}
}
oldChildNodeList = newChildrenNodeList;
}
}
});
// 2.判断/config节点是否存在,不存在则创建
if (zk.exists(PARENT_NODE, null) == null) {
// 创建一个永久节点
zk.create(PARENT_NODE, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
// 3.添加监听
oldChildNodeList = zk.getChildren(PARENT_NODE, true);
for (String childNode : oldChildNodeList) {
// 将所有的子节点数据变更也加上监听
String childNodePath = PARENT_NODE + "/" + childNode;
zk.getData(childNodePath, true, null);
}
/**
* 第四步: 让程序一致运行 当前的代码的执行分为两个线程运行 主线程 + 监听线程 (根据event的数据来判断掉地调用哪个watcher中的process方法运行) 最后的那些打印值都是
* process方法中打印出来, 而且process方法的调用运行不在主线程中
*/
Thread.sleep(Long.MAX_VALUE);
}
public static String getDiffBetweenTwoList(List<String> smallList, List<String> bigList) {
// 默认实现: 增加了一个节点
List<String> big = bigList;
List<String> small = smallList;
for (String val : big) {
if (!small.contains(val)) {
return val;
}
}
return null;
}
}
package de.apps.config;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* Copyright (c) 2019 leyou ALL Rights Reserved Project: learning Package: app.配置管理 Version: 1.0
*
* @author qingzhi.wu
* @date 2020/6/21 11:24
*/
public class Client {
private static final String Connect_String =
"cdh01:2181,cdh02:2181,cdh03:2181";
private static final int Session_Timeout = 4000;
private static final String PARENT_NODE = "/config";
private static final String key = "name";
private static final String value = "huangbo";
private static final String value_new = "huangbo_copy";
public static void main(String[] args) throws Exception {
/** 第一步: 获取 zookeeper 连接 */
ZooKeeper zk = new ZooKeeper(Connect_String, Session_Timeout, null);
/** 第二步: 先判断 /config 节点存在与否 */
Stat exists = zk.exists(PARENT_NODE, null);
if (exists == null) {
zk.create(PARENT_NODE, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/** 第三部: 模拟实现 添加 修改 删除 */
String path = PARENT_NODE + "/" + key;
// 第三部: 增加一项配置
// zk.create(path, value.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 第三部: 修改一项配置
// zk.setData(path, value_new.getBytes(), -1);
// 第三部: 删除一项配置
zk.delete(path, -1);
zk.close();
}
}