Zookeeper应用总结
1.API使用
api基本操作
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.zkutil;
import java.util.List;
import org.apache.zookeeper.ZooKeeper;
/**
* 描述: ZooKeeper API 测试类
*/
public class ZKUtilTest {
// 获取zookeeper连接时所需要的服务器连接信息,格式为主机名:端口号
private static final String ConnectString = "hadoop03:2181,hadoop04:2181,hadoop05:2181";
// 请求了解的会话超时时长
private static final int SessionTimeout = 5000;
public static void main(String[] args) throws Exception {
ZooKeeper zk = ZKUtil.getZKConnection(ConnectString, SessionTimeout);
String path = "/aa";
String value = "aa";
// 创建节点
String createZKNode = ZKUtil.createZKNode("/aa/ee/aa", value, zk);
System.out.println(createZKNode + "\t" + createZKNode != null ? "创建节点成功" : "创建节点失败");
// 获取节点数据
System.out.println(ZKUtil.getZNodeData(path, zk));
// 判断节点存在不存在
System.out.println(ZKUtil.existsZNode(path, zk));
// 获取子节点列表
List<String> childrenZNodes = ZKUtil.getChildrenZNodes(path, zk);
for (String znodePath : childrenZNodes) {
System.out.println(znodePath);
}
// 修改节点数据
System.out.println(ZKUtil.updateZNodeData(path, "abcd", zk));
}
}
</span>
api加强
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.zkutil;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
/**
* 编程思维训练
* 1、级联查看某节点下所有节点及节点值
* 2、删除一个节点,不管有有没有任何子节点
* 3、级联创建任意节点
* 4、清空子节点
*/
public class ZKUtil2 {
/**
* 级联查看某节点下所有节点及节点值
*/
public static void getChildNodeAndValue(String path,Map<String,String> map) throws Exception{
//如果存在该节点
if(zk.exists(path, false) != null){
map.put(path,new String(zk.getData(path, false, null)));
List<String> list = zk.getChildren(path, false);
if( list.size()!=0 ){
for(String child:list){
if(path.equals("/")){
getChildNodeAndValue(path+child,map);
}else{
getChildNodeAndValue(path+"/"+child,map);
}
}
}
}
}
/**
* 删除一个节点,不管有有没有任何子节点
*/
public static boolean rmr(String path, ZooKeeper zk) throws Exception {
List<String> children = zk.getChildren(path, false);
if (children.size() == 0) {
// 删除节点
zk.delete(path, -1);
} else {
// 要删除这个有子节点的父节点,那么就需要先删除所有子节点,
// 然后再删除该父节点,完成对该节点的级联删除
// 删除有子节点的父节点下的所有子节点
for (String nodeName : children) {
rmr(path + "/" + nodeName, zk);
}
// 删除该父节点
rmr(path, zk);
}
return true;
}
/**
* 级联创建任意节点
*/
public static boolean createZNode(String znodePath, String data, ZooKeeper zk) throws Exception {
// 首先判断该节点是否存在,如果存在,则不创建, return false
if (zk.exists(znodePath, null) != null) {
return false;
} else {
try {
// 直接创建,如果抛异常,则捕捉异常,然后根据对应的异常如果是发现没有父节点,那么就创建父节点
zk.create(znodePath, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} catch (KeeperException e) {
// 截取父节点
String parentPath = znodePath.substring(0, znodePath.lastIndexOf("/"));
// 创建父节点
createZNode(parentPath, parentPath, zk);
try {
// 父节点创建好了之后,创建该节点
zk.create(znodePath, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} catch (KeeperException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
/**
* 清空子节点
*/
public static boolean clearChildNode(String znodePath, ZooKeeper zk) throws Exception {
List<String> children = zk.getChildren(znodePath, null);
for (String child : children) {
String childNode = znodePath + "/" + child;
if (zk.getChildren(childNode, null).size() != 0) {
clearChildNode(childNode, zk);
}
zk.delete(childNode, -1);
}
return true;
}
}
</span>
watcher的使用
<span style="font-family:KaiTi_GB2312;">package com.ghgj.zk.watch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
/**
*
* 描述:讲解 watcher的使用
*/
public class MyFirstZKWithWatch {
private static final String Connect_String = "hadoop02:2181,hadoop03:2181,hadoop04:2181,hadoop05:2181";
private static final int Session_Timeout = 4000;
public static void main(String[] args) throws Exception{
/**
* 匿名对象的 使用场景 是 仅仅 只需要使用这个 接口中的实现类的一个 对象即可
*/
ZooKeeper zk = new ZooKeeper(Connect_String, Session_Timeout, new Watcher(){
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
// System.out.println("SSSSSSSSSS");
/**
* 第一: 如果 state 有值, 但是 type 和 path无值, 那就证明是在获取连接
*
* 第二: type和path的意义就是告诉 监听器的 process 方法
* 到底是哪个 path的那个事件被响应了。 以便process方法能够调用对应的业务逻辑的代码执行
*/
EventType type = event.getType();
String path = event.getPath();
KeeperState state = event.getState();
// System.out.println(path + "\t" + type + "\t" + state);
if(path == null && state.getIntValue() == 3){
System.out.println("获取到连接成功");
}else{
System.out.println(path + "这个节点的" + type + "被触发了。");
}
}
});
// ZooKeeper zk = new ZooKeeper(Connect_String, Session_Timeout, new MyFirstCommonWatch());
// ZooKeeper zk = new ZooKeeper(Connect_String, Session_Timeout, null);
/**
* 先来添加监听: 对哪个节点的哪个事件感兴趣
*
*
* 节点: "/name"
* 事件: NodeChildrenChanged
*/
// zk.getChildren("/name", new MyFirstCommonWatch());
zk.getChildren("/name", true);
// zk.getChildren("/name", false);
/**
* 触发监听
*/
zk.create("/name/ss2", "ss1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
zk.close();
}
}
</span>
2.分布式锁
1、需求描述 在我们自己的分布式业务系统中,可能会存在某种资源,需要被整个系统的各台服务器共享 访问,但是只允许一台服务器同时访问
2、设计思路
1、设计多个客户端同时访问同一个数据
2、为了同一时间只能允许一个客户端上去访问,所以各个客户端去 zookeeper 集群的一个 znode 节点去注册一个临时节点,定下规则,每次都是编号最小的客户端才能去访问
3、多个客户端同时监听该节点,每次当有子节点被删除时,就都收到通知,然后判断自己 的编号是不是最小的,最小的就去执行访问,不是最小的就继续监听。
3、代码开发
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.app.distributeLock;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* 需求:多个客户端,需要同时访问同一个资源,但同时只允许一个客户端进行访问。
* 设计思路:多个客户端都去父znode下写入一个子znode,能写入成功的去执行访问,写入不成功的等待
*/
public class MyDistributeLock {
private static final String connectStr = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
private static final int sessionTimeout = 4000;
private static final String PARENT_NODE = "/parent_locks";
private static final String SUB_NODE = "/sub_client";
static ZooKeeper zk = null;
private static String currentPath = "";
public static void main(String[] args) throws Exception {
MyDistributeLock mdc = new MyDistributeLock();
// 1、拿到zookeeper链接
mdc.getZookeeperConnect();
// 2、查看父节点是否存在,不存在则创建
Stat exists = zk.exists(PARENT_NODE, false);
if (exists == null) {
zk.create(PARENT_NODE, PARENT_NODE.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
// 3、监听父节点
zk.getChildren(PARENT_NODE, true);
// 4、往父节点下注册节点,注册临时节点,好处就是,当宕机或者断开链接时该节点自动删除
currentPath = zk.create(PARENT_NODE + SUB_NODE, SUB_NODE.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
Thread.sleep(Long.MAX_VALUE);
// 5、关闭zk链接
zk.close();
}
/**
* 拿到zookeeper集群的链接
*/
public void getZookeeperConnect() throws Exception {
zk = new ZooKeeper(connectStr, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 匹配看是不是子节点变化,并且监听的路径也要对
if (event.getType() == EventType.NodeChildrenChanged && event.getPath().equals(PARENT_NODE)) {
try {
// 获取父节点的所有子节点, 并继续监听
List<String> childrenNodes = zk.getChildren(PARENT_NODE, true);
// 匹配当前创建的znode是不是最小的znode
Collections.sort(childrenNodes);
if ((PARENT_NODE + "/" + childrenNodes.get(0)).equals(currentPath)) {
// 处理业务
handleBusiness(currentPath);
}else{
System.out.println("not me");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
public void handleBusiness(String create) throws Exception {
System.out.println(create + " is working......");
// 线程睡眠0-4秒钟,是模拟业务代码处理所消耗的时间
Thread.sleep(new Random().nextInt(4000));
// 模拟业务处理完成
zk.delete(currentPath, -1);
System.out.println(create + " is done ......");
// 线程睡眠0-4秒, 是为了模拟客户端每次处理完了之后再次处理业务的一个时间间隔,最终的目的就是用来打乱你运行的多台
// 服务器抢注该子节点的顺序
Thread.sleep(new Random().nextInt(4000));
// 模拟去抢资源锁
currentPath = zk.create(PARENT_NODE + SUB_NODE, SUB_NODE.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
}
}</span>
3.服务器上下线动态感知
1、需求描述 某分布式系统中,主节点可以有多台,可以动态上下线。任意一台客户端都能实时感知 到主节点服务器的上下线
2、设计思路
1、设计服务器端存入服务器上线,下线的信息,比如都写入到 servers 节点下
2、设计客户端监听该 servers 节点,获取该服务器集群的在线服务器列表
3、服务器一上线,就往 zookeeper 文件系统中的一个统一的节点比如 servers 下写入一个临 时节点,记录下服务器的信息(思考,该节点最好采用什么类型的节点?)
4、服务器一下线,则删除 servers 节点下的该服务器的信息,则客户端因为监听了该节点的 数据变化,所以将第一时间得知服务器的在线状态
3、代码开发
代码1:
服务器端处理:
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.mydemo;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* * 用来模拟服务器的动态上线下线 * 总体思路就是服务器上线就上 zookeeper 集群创建一个临时节点,然后监听了该数据节
* 点的个数变化的客户端都收到通知 * 下线,则该临时节点自动删除,监听了该数据节点的个数变化的客户端也都收到通知
*/
public class DistributeServer {
private static final String connectStr = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
private static final int sessionTimeout = 4000;
private static final String PARENT_NODE = "/server";
static ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
DistributeServer distributeServer = new DistributeServer();
distributeServer.getZookeeperConnect();
distributeServer.registeServer("hadoop03");
Thread.sleep(Long.MAX_VALUE);
}
/** * 拿到 zookeeper 进群的链接 */
public void getZookeeperConnect() throws Exception {
zk = new ZooKeeper(connectStr, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
}
});
}
/** * 服务器上线就注册,掉线就自动删除,所以创建的是临时顺序节点 */
public void registeServer(String hostname) throws Exception {
Stat exists = zk.exists(PARENT_NODE, false);
if (exists == null) {
zk.create(PARENT_NODE, "server_parent_node".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
zk.create(PARENT_NODE + "/" + hostname, hostname.getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + " is online, start working......");
}
}
</span>
客户端处理:
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.mydemo;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
/**
* * 用来模拟用户端的操作:连上 zookeeper 进群,实时获取服务器动态上下线的节点信息 * 总体思路就是每次该 server
* 节点下有增加或者减少节点数,我就打印出来该 server 节点 下的所有节点
*/
public class DistributeClient {
private static final String connectStr = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
private static final int sessionTimeout = 4000;
private static final String PARENT_NODE = "/server";
static ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
DistributeClient dc = new DistributeClient();
dc.getZookeeperConnect();
Thread.sleep(Long.MAX_VALUE);
}
/** * 拿到 zookeeper 进群的链接 */
public void getZookeeperConnect() throws Exception {
zk = new ZooKeeper(connectStr, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
try { // 获取父节点 server 节点下所有子节点,即是所有正上线服务的服 务器节点 List<String>
// children = zk.getChildren(PARENT_NODE, true);
// List<String> servers = new ArrayList<String>();
// for(String child: children){ // 取出每个节点的数据,放入到 list 里
// String server = new
// String(zk.getData(PARENT_NODE+"/"+child, false,
// null), "UTF-8"); servers.add(server); } // 打印 list
// 里面的元素
System.out.println(servers);
} catch (Exception e) {
e.printStackTrace();
}
}
});
System.out.println("Client is online, start Working......");
}
}</span>
代码2
namenode:
<span style="font-family:KaiTi_GB2312;">package com.zk.watch.app.servers;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* 描述:
* 就是模拟HDFS中的namenode角色
* 监听/servers节点下的子节点的列表
* 使用/servers节点下的一个子节点的创建和删除来模拟实现一个datnaode节点的上线和下线
* 当前的namenode程序是必须一直运行,一直 监听着 /servers节点
* 利用线程的睡眠来实现
* 做到循环监听
*/
public class Namenode {
private static final String Connect_String = "hadoop02:2181,hadoop03:2181,hadoop04:2181,hadoop05:2181";
private static final int Session_Timeout = 4000;
private static final String PARENT_NODE = "/servers";
static ZooKeeper zk = null;
static List<String> oldChildNodeList = null;
public static void main(String[] args) throws Exception {
/**
* 第一步: 获取zookeeper连接
*/
zk = new ZooKeeper(Connect_String, Session_Timeout, new Watcher(){
@Override
public void process(WatchedEvent event) {
EventType type = event.getType();
String path = event.getPath();
KeeperState state = event.getState();
if(state.getIntValue() == 3 && path != null ) {
/**
* 第五步: 具体的相对应的业务逻辑代码
* 业务逻辑代码的具体编写 是应该根据 event中的参数来决定
* 如果这个判断条件满足你的要求,,那么就调用你的业务逻辑代码执行
* if中的判断条件其实就是 type和path
*/
if(path.equals(PARENT_NODE) && type == EventType.NodeChildrenChanged){
List<String> newChildNodeList = null;
/**
* 第六步: 做到循环监听
*
* 当前这句代码的作用有两个:
* 1、添加监听
* 2、获取/servers节点下的子节点的列表
*/
try {
newChildNodeList = zk.getChildren(PARENT_NODE, true);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
String flag = "上线";
if(newChildNodeList.size() < oldChildNodeList.size()){
flag = "下线";
}
String server = getDiffBetweenTwoList(oldChildNodeList, newChildNodeList);
// 当前这个if里面的所有操作都是针对 /servers 节点下 多了一个节点或者少了一个节点之后的 响应
// 做一个模拟实现: 来查询当前上线或者下线的那台服务器到底是谁。
System.out.println("当前模拟实现的 HDFS 集群中 " + flag + " 了一个台服务器:" + server );
/**
* 每次 /servers节点下的 子节点的列表发生了改变之后 ,都需要去更新 oldChildNodeList
*/
oldChildNodeList = newChildNodeList;
}
}
}
});
/**
* 第二步: 判断/servers节点存在与否。如果不存在,先创建,然后添加监听
*/
Stat exists = zk.exists(PARENT_NODE, null);
if(exists == null){
zk.create(PARENT_NODE, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 第三步: 给/servers节点添加 NodeChildrenChanged事件的监听
*/
oldChildNodeList = zk.getChildren(PARENT_NODE, true);
/**
* 第四步: 模拟当前的namendeo一直运行
*/
Thread.sleep(Long.MAX_VALUE);
}
/**
* 当前这个方法的作用: 是用来获取两个集合中的相差的那个节点
* 方式:遍历大的集合, 然后去小集合中去匹配,看是否存储, 如果不存在,就返回值就是他。!!!!
*/
public static String getDiffBetweenTwoList(List<String> oldChildNodeList, List<String> newChildNodeList){
// 默认实现: 增加了一个节点
List<String> big = newChildNodeList;
List<String> small = oldChildNodeList;
if(newChildNodeList.size() < oldChildNodeList.size()){
big = oldChildNodeList;
small = newChildNodeList;
}
for(String val : big){
if(!small.contains(val)){
return val;
}
}
return null;
}
}</span>
datanode:
<span style="font-family:KaiTi_GB2312;">package com.zk.watch.app.servers;
import java.io.IOException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* 描述:
* 实现两个功能:
* 1、创建一个/servers节点下的子节点来模拟实现一个datanode的上线
* 2、删除一个/servers节点下的子节点来模拟实现一个datanode的下线
* 利用zookeeper系统中的znode的临时性
* 如果datnoade一直在运行着,那么就请一直维护zookeeper的一个会话连接
* 假如代表hadoop02这个服务器节点的znode节点/servers/hadoop02是临时节点。当真正的
* 服务器hadoop02宕机之后, zookeeper会意识到=当前这个临时节点的创建会话已经断开,所以zookeeper会自动删除该临时节点
*
* 删除了该临时节点。那么监听/servers节点下的子节点变化的namenode程序就能瞬时感知到有一个datanode下线了。!!!!
*/
public class Datanode {
private static final String Connect_String = "hadoop02:2181,hadoop03:2181,hadoop04:2181,hadoop05:2181";
private static final int Session_Timeout = 4000;
private static final String PARENT_NODE = "/servers";
private static final String DATA_NODE = "hadoop06";
public static void main(String[] args) throws Exception {
/**
* 第一步: 获取zookeeper连接
*/
ZooKeeper zk = new ZooKeeper(Connect_String, Session_Timeout, null);
/**
* 第二步: 判断/servers节点存在与否。如果不存在,先创建,然后添加监听
*/
Stat exists = zk.exists(PARENT_NODE, null);
if(exists == null){
zk.create(PARENT_NODE, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 第三步: 利用创建者删除/servers节点下的子节点来模拟实现datanode的上线和下线
*/
zk.create(PARENT_NODE +"/"+ DATA_NODE, DATA_NODE.getBytes(),
Ids.OPEN_ACL_UNSAFE
, CreateMode.EPHEMERAL);
System.out.println(DATA_NODE + " 这台服务器上线了。");
/**
* 第四步: 模拟当前上线的这个datnoade一直在运行着
*
* 停止掉当前的这个datanode程序就意味着当前这个节点的会话就断开了。
*/
Thread.sleep(Long.MAX_VALUE);
// zk.close();
}
}</span>
代码3:
ServerWorkServerStart:服务器上线
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.app.serverList;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* 用来处理服务器上下线往zookeeper写入或者删除数据用的。
* 它就是我们抽象出来的一个台服务器,用于服务器上线
*/
public class ServerWorkServerStart {
private static final String connectString = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
private static final int sessionTimeout = 4000;
// 上线的服务器列表的父节点
private static final String PARENT_NODE = "/servers";
// 上线服务器
private static final String server = "hadoop03";
private static final String SUB_NODE = server;
public static void main(String[] args) throws Exception {
// 拿zookeeper的请求链接
ZooKeeper zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
}
});
/**
* 为了在第一台服务器上线的时候找不到父节点,去先把这个父节点给创建出来
*/
Stat exists = zk.exists(PARENT_NODE, false);
if (exists == null) {
zk.create(PARENT_NODE, "servers_parent_node".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
// 拿到zookeeper链接之后,就往我们的父znode节点下去写入一台服务器的信息,写入
// 的znode类型是临时节点
zk.create(PARENT_NODE + "/" + SUB_NODE, server.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(server + " is online。。。。。。。。。");
zk.close();
}
}
</span>
ServerWorkServerStop:服务器下线
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.app.serverList;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* 用来处理服务器上下线往zookeeper写入或者删除数据用的。
* 它就是我们抽象出来的一个台服务器,用于服务器下线
*/
public class ServerWorkServerStop {
private static final String connectString = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
private static final int sessionTimeout = 4000;
private static final String PARENT_NODE = "/servers";
private static final String server = "hadoop04";
public static void main(String[] args) throws Exception {
// 拿zookeeper的请求链接
ZooKeeper zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
}
});
/**
* 为了在第一台服务器上线的时候找不到父节点,去先把这个父节点给创建出来
*/
Stat exists = zk.exists(PARENT_NODE, false);
if (exists == null) {
zk.create(PARENT_NODE, "servers_parent_node".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 服务器下线程序
*/
zk.delete(PARENT_NODE + "/" + server, -1);
zk.close();
}
}</span>
ServerWorkClient:用户的客户端程序
<span style="font-family:KaiTi_GB2312;">package com.zookeeper.app.serverList;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooKeeper;
/**
* 这就是用户的客户端程序,用来监控服务器集群到底有哪些服务器在工作
*/
public class ServerWorkClient {
private static final String connectString = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
private static final int sessionTimeout = 4000;
private static final String PARENT_NODE = "/servers";
static ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
/**
* 1、拿zookeeper的服务器链接
*/
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
String path = event.getPath();
EventType et = event.getType();
/**
* 收到监听通知的时候,就去查询servers节点下有哪些服务器在线
*/
if(path.equals(PARENT_NODE) && et == EventType.NodeChildrenChanged){
/**
* 2、获取父节点servers下有哪些子节点,并且把子节点的数据给拿出来
*/
List<String> hostList = getServerList();
// [hadoop01,hadoop02]
System.out.println(hostList);
}else{
System.out.println("当前事件跟我服务器动态上下线没有任何关系");
}
}
});
zk.getChildren(PARENT_NODE, true);
Thread.sleep(Long.MAX_VALUE);
zk.close();
}
/**
* 用来获取当前在线服务器列表
*/
public static List<String> getServerList(){
List<String> hostList = new ArrayList<String>();
try {
// 这句话是为了获取servers节点下的子节点
List<String> children = zk.getChildren(PARENT_NODE, true);
for(String znode: children){
byte[] data = zk.getData(PARENT_NODE+"/"+znode,false, null);
hostList.add(new String(data));
}
} catch (Exception e) {
e.printStackTrace();
}
return hostList;
}
}
</span>
4.配置管理
代码1:
Client: 模拟一个真正用来对配置信息进行 添加 修改 删除的操作的 客户端
package com.zk.watch.app.config;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
/**
*
*
* 描述:
*
* 作用: 模拟一个真正用来对配置信息进行 添加 修改 删除的操作的 客户端
*/
public class Client {
private static final String Connect_String = "hadoop02:2181,hadoop03:2181,hadoop04:2181,hadoop05: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(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 第三部: 模拟实现 添加 修改 删除
*/
String path = PARENT_NODE + "/" + key;
// 增加一项配置
// zk.create(path , value.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 删除一项配置
// zk.delete(path, -1);
// 修改一项配置
zk.setData(path, value_new.getBytes(), -1);
zk.close();
}
}
server:模拟了一个服务器节点的监听程序
package com.zk.watch.app.config;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
*
*
* 描述:
*
* 一个server实例就代表了一个 外部集群的 一个 服务器节点
*
* 就是用来监听配置信息的改变(到底是改变了什么)
*
* server程序其实就是模拟了一个服务器节点的监听程序
*/
public class Server {
private static final String Connect_String = "hadoop02:2181,hadoop03:2181,hadoop04:2181,hadoop05:2181";
private static final int Session_Timeout = 4000;
private static final String PARENT_NODE = "/config";
static List<String> oldChildNodeList = null;
static ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
/**
* 第一步: 获取zookeeper连接
*/
zk = new ZooKeeper(Connect_String, Session_Timeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
EventType type = event.getType();
String path = event.getPath();
KeeperState state = event.getState();
/**
* path : /config + /config/name
* type : NodeChildrenChanged NodeDataChanged NodeDeleted
*/
/**
* 当前这个判断的作用就是用来屏蔽获取连接时的那个触发
*
* type; None
* path: null
*/
if (state.getIntValue() == 3 && path != null) {
// 触发了监听之后的 子节点的列表信息
List<String> newChildNodeList = null;
try {
newChildNodeList = zk.getChildren(PARENT_NODE, true);
} catch (KeeperException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
/**
* 虽然此处响应的逻辑有可能是 添加 或者删除, 但是经过判断之后,如果是删除,不做任何处理
* 如果添加,那么做对应的业务处理
*/
if (path.equals(PARENT_NODE) && type == EventType.NodeChildrenChanged) {
// 仅仅只是处理添加的逻辑
if(oldChildNodeList.size() < newChildNodeList.size()){
String diffServer = getDiffBetweenTwoList(oldChildNodeList, newChildNodeList);
byte[] data = null;
try {
data = zk.getData(PARENT_NODE + "/" + diffServer, true, null);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("服务器集群 add 了一项配置: key="+diffServer+", value="+new String(data));
}
/**
* 父节点下的一个子节点的删除 (删除)
*
* 删除子节点 ===== 删除一项配置
*/
} else {
for (String child : oldChildNodeList) {
/**
* childPath到底应该怎么获取? 获取触发之前 process方法执行之前。。 也就是
* 客户端的 删除 和 修改的操作之前的
*/
String childPath = PARENT_NODE + "/" + child;
if (path.equals(childPath) && type == EventType.NodeDeleted) {
// 删除的配置的key
String diffServer = getDiffBetweenTwoList(newChildNodeList, oldChildNodeList);
System.out.println("服务器集群 delete 了一项配置: key="+diffServer);
}
}
}
/**
* 父节点下的一个子节点的数据变化事件 (修改)
*/
for (String child : oldChildNodeList) {
String childPath = PARENT_NODE + "/" + child;
if (path.equals(childPath) && type == EventType.NodeDataChanged) {
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 = newChildNodeList;
}
}
});
/**
* 第二步: 先判断 /config 节点存在与否
*/
Stat exists = zk.exists(PARENT_NODE, null);
if (exists == null) {
zk.create(PARENT_NODE, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 第三步:添加监听
*
* 监听有两种: 1、/config节点的 NodeChildrenChanged 2、/config节点下的子节点的
* NodeDataChanged
*/
oldChildNodeList = zk.getChildren(PARENT_NODE, true);
for (String child : oldChildNodeList) {
String childPath = PARENT_NODE + "/" + child;
// 添加子节点的 NodeDataChanged
zk.getData(childPath, 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;
}
}
代码2:
ClusterConfigServer
package com..zookeeper.app.clusterConfig;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class ClusterConfigServer {
// 获取zookeeper连接时所需要的服务器连接信息,格式为主机名:端口号
private static final String ConnectString = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
// 请求链接的会话超时时长
private static final int SessionTimeout = 5000;
private static ZooKeeper zk = null;
private static final String PARENT_NODE = "/config";
public static void main(String[] args) throws Exception {
zk = new ZooKeeper(ConnectString, SessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event.getPath() + "\t-----" + event.getType());
}
});
// 创建父节点
if (null == zk.exists(PARENT_NODE, false)) {
zk.create(PARENT_NODE, "cluster-config".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
// 预先建立几个配置信息
// zk.create(PARENT_NODE + "/hadoop", "hadoop".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// zk.create(PARENT_NODE+"/hive", "hive".getBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// zk.create(PARENT_NODE+"/mysql", "mysql".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// zk.create(PARENT_NODE+"/redis", "redis".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.delete(PARENT_NODE+"/hadoop", -1);
// zk.delete(PARENT_NODE+"/hive", -1);
// zk.delete(PARENT_NODE+"/mysql", -1);
// zk.delete(PARENT_NODE+"/redis", -1);
// zk.setData(PARENT_NODE+"/hadoop", "eee".getBytes(), -1);
// zk.setData(PARENT_NODE+"/hive", "3456".getBytes(), -1);
// zk.setData(PARENT_NODE+"/mysql", "34562".getBytes(), -1);
// zk.setData(PARENT_NODE+"/redis", "3456333".getBytes(), -1);
zk.close();
}
}
ClusterConfigClient
package com.zookeeper.app.clusterConfig;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooKeeper;
public class ClusterConfigClient {
// 获取zookeeper连接时所需要的服务器连接信息,格式为主机名:端口号
private static final String ConnectString = "hadoop02:2181,hadoop03:2181,hadoop04:2181";
// 请求了解的会话超时时长
private static final int SessionTimeout = 5000;
private static ZooKeeper zk = null;
private static final String PARENT_NODE = "/config";
private static Map<String, String> configMap = new HashMap<String, String>();
public static void main(String[] args) throws Exception {
// 第一步:获取zk链接
zk = new ZooKeeper(ConnectString, SessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// System.out.println(event.getPath() + "\t-----" + event.getType());
// 增加或者删除的逻辑
if (event.getPath().equals(PARENT_NODE) && event.getType() == EventType.NodeChildrenChanged) {
try {
// 当监听一收到通知,就表示有子节点变化事件,那么就先找出现在还剩下的配置
Map<String, String> currentConfigMap = getConfigMap(PARENT_NODE);
// 用当前还剩下的配置, 跟之前的配置做对比,找出是新增加的配置,还是删除的配置
// 首先确定下来是新增加了一个配置还是删除了一个配置
String doFlag = "add";
Map<String, String> bigMap = currentConfigMap;
Map<String, String> smallMap = configMap;
if (currentConfigMap.size() < configMap.size()) {
doFlag = "delete";
bigMap = configMap;
smallMap = currentConfigMap;
}
Set<String> smallMapKeySet = smallMap.keySet();
// 不管是新增加一个配置还是删除一个配置,我都用大的map的元素去小的map里找,没找出结果的就是多出来的
for (String key : bigMap.keySet()) {
if (!smallMapKeySet.contains(key)) {
System.out.println("当前 " + doFlag + " 了一项配置,该配置节点名称是:" + key);
// 如果是新增, 那么还得为新增加的节点,添加NodeDataChanged监听
if (doFlag.equals("add")) {
zk.getData(key, true, null);
}
break;
}
}
// 最后一定要注意,map的迭代
configMap = currentConfigMap;
} catch (Exception e) {
e.printStackTrace();
}
}
// 假如是修改配置怎么办。?
for (String key : configMap.keySet()) {
if (event.getPath().equals(key) && event.getType() == EventType.NodeDataChanged) {
try {
String lastValue = configMap.get(key);
// 获取该节点的数据,并且持续加监听, 没有被更改过数据的不用加监听,反正没有被触发
byte[] data = zk.getData(key, true, null);
System.out.println("节点 " + key + "的配置信息" + new String(lastValue) + "被改成了:" + new String(data));
configMap = getConfigMap(PARENT_NODE);
break;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
});
/**
* 第二步, 监听程序一上线, 先获取到所有的config信息,
* 如果在方法中就加了各种监听, 那么下面两段话就可以注释掉了。
*/
configMap = getConfigMap(PARENT_NODE);
Thread.sleep(Long.MAX_VALUE);
zk.close();
}
/**
* 获取PARENT_NODE节点下所有的配置信息放到一个map中
*/
public static Map<String, String> getConfigMap(String znode) throws Exception {
Map<String, String> map = new HashMap<String, String>();
// 给父节点加NodeChildrenChanged事件
List<String> children = zk.getChildren(znode, true);
for (String child : children) {
byte[] data = zk.getData(znode + "/" + child, false, null);
map.put(znode + "/" + child, new String(data));
// 给子节点加NodeDataChanged事件
zk.getData(znode + "/" + child, true, null);
}
return map;
}
}
5.同步队列
SyncQueueServer:用来记录每一台上线的服务器
package com.zookeeper.app.syncQueue;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* 作用:用来记录每一台上线的服务器
* 具体做法:往zookeeper的文件系统里面的某一个znode下面写入我们上线了的服务器的信息
* @author Administrator
*
* zookeeper + duboo 管理业务 service
*
* 上面注册
*
* public List<String> getServersList(int flag);
*/
public class SyncQueueServer {
private static final String CONNECT_STRING = "hadoop03:2181,hadoop02:2181";
private static final int sessionTimeout = 4000;
private static final String PARENT_NODE = "/syncQueue";
private static final String HOSTNAME = "hadoop05";
public static void main(String[] args) throws Exception {
/**
* 1、获取zookeeper的链接
*/
ZooKeeper zk = new ZooKeeper(CONNECT_STRING, sessionTimeout, new Watcher() {
/**
* 注意:这个监听器会监听zookeeper所有的事件
*/
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
}
});
/**
* 2、先判断父节点是否存在
*/
Stat exists = zk.exists(PARENT_NODE, false);
if (exists == null) {
zk.create(PARENT_NODE, PARENT_NODE.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} else {
System.out.println(PARENT_NODE + " 已存在,不用我创建");
}
/**
* 3、往父节点下记录一台刚上线的服务器的信息
* 节点的名字:/syncQueue/hadoop01
*/
String path = zk.create(PARENT_NODE + "/" + HOSTNAME, HOSTNAME.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println("当前上线的服务器是:" + HOSTNAME + ", 当前服务器注册的子节点是:" + path);
Thread.sleep(Long.MAX_VALUE);
zk.close();
}
}
SyncQueueClient:我们执行业务方法的业务类
package com.zookeeper.app.syncQueue;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.ZooKeeper;
/**
* 作用:我们执行业务方法的业务类,监听PARENT_NODE下的子节点的数目 因为我们是实现同步队列,要求是达到一定的队列成员时,我们的业务方法才可以执行
*/
public class SyncQueueClient {
private static final String CONNECT_STRING = "hadoop01:2181,hadoop02:2181";
private static final int sessionTimeout = 4000;
private static final String PARENT_NODE = "/syncQueue";
private static final int NEED_QUEUE = 3;
static ZooKeeper zk = null;
static int count = 0;
public static void main(String[] args) throws Exception {
/**
* 1、获取zookeeper链接
*/
zk = new ZooKeeper(CONNECT_STRING, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
String path = event.getPath();
EventType et = event.getType();
// 第一次判断监听的节点和事件是否满足要求
if (path.equals(PARENT_NODE) && et == EventType.NodeChildrenChanged) {
// 第二次要判断队列的成员是否都达到,如果是,才能执行业务方法
try {
List<String> children = zk.getChildren(PARENT_NODE, true);
int queueNumber = children.size();
if (queueNumber == NEED_QUEUE) {
handleBusiness(true);
} else if (queueNumber < NEED_QUEUE) {
if(count == NEED_QUEUE){
handleBusiness(false);
}else{
System.out.println("正等待其他兄弟上线。。。。。。。");
}
}
count = queueNumber;
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
/**
* 2、先判断父节点是否存在
*/
Stat exists = zk.exists(PARENT_NODE, false);
if (exists == null) {
zk.create(PARENT_NODE, PARENT_NODE.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} else {
System.out.println(PARENT_NODE + " 已存在,不用我创建");
}
/**
* 3、给该程序应该要监听的znode加监听
*/
zk.getChildren(PARENT_NODE, true);
Thread.sleep(Long.MAX_VALUE);
zk.close();
}
/**
* 首先,我们的这个程序会一直监听PARENT_NODE,等待所监听的节点下的队列成员是否全部到达 如果满足条件,该业务方法才会执行
*
* 这个条件是: 1、该业务程序收到的某一个节点的某一个事件要满足我们监听的该节点 2、队列的所有成员都达到
*
* @throws Exception
*/
public static void handleBusiness(boolean flag) throws Exception {
/*while (flag) {
System.out.println("正在处理业务方法........");
Thread.sleep(3000);
}*/
if(flag){
System.out.println("正在处理业务方法........");
}else{
System.out.println("已停止处理业务");
}
}
}