1、需求描述
在我们自己的分布式业务系统中,可能会存在某种资源,需要被整个系统的各台服务器共享访问,但是只允许一台服务器同时访问。
2、设计思路
(1)设计多个客户端同时访问同一个数据;
(2)为了同一时间只能允许一个客户端上去访问,所以各个客户端取 zookeeper 集群的一个 znode 节点去注册一个临时节点,定下规则,每次都是编号最小的客户端才能去访问;
(3)多个客户端同时监听该节点,每次当有子节点被删除时,就都收到通知,然后判断自己的编号是不是最小的,最小的就去执行访问,不是最小的就继续监听。
3、代码开发
(1)加入依赖:
<dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
</dependencies>
(2)代码实现:
package com.zc.bigdata.demo2;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.Collections;
import java.util.List;
import java.util.Random;
/**
* @作者: zc
* @时间: 2021/3/4 15:09
* @描述: 需求:多个客户端,需要同时访问同一个资源,但同时只允许一个客户端进行访问。
* 设计思路:多个客户端都去父 znode 下写入一个子 znode,
* 能写入成功的去执行访问,写入不成功的等待
*/
public class MyDistributeLock {
// 链接地址
private static final String connectStr = "hadoop01:2181,hadoop02:2181,hadoop03:2181,hadoop04:2181,hadoop05:2181";
// 链接请求时间
private static final int sessionTimeout = 4000;
// 准备的测试分布式共享锁的 ZK 父znode
private static final String PARENT_NODE = "/parent_locks";
private static final String SUB_NODE = "/sub_client";
// 初始化 ZK 操作对象
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(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
// 3、监听父节点
zk.getChildren(PARENT_NODE, true);
// 4、往父节点下注册节点,注册临时节点,好处就是,当宕机或者断开链接时该节点自动删除
currentPath = zk.create(PARENT_NODE+SUB_NODE, SUB_NODE.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// 5、关闭 zk 链接
Thread.sleep(Long.MAX_VALUE);
zk.close();
}
/**
* 拿到 zookeeper 集群的链接
*/
public void getZookeeperConnect() throws Exception {
zk = new ZooKeeper(connectStr, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 匹配看是不是子节点变化,并且监听的路径也要对
if(event.getType() == Event.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);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
public void handleBusiness(String create) throws Exception{
System.out.println(create+" is working......");
Thread.sleep(new Random().nextInt(4000));
zk.delete(currentPath, -1);
System.out.println(create+" is done ......");
currentPath = zk.create(PARENT_NODE+SUB_NODE, SUB_NODE.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
(3)运行:
4、学习内容
上节学习内容:Zookeeper 应用案例(一)之服务器上下线动态感知
下节学习内容:Hadoop2.7.5 高可用(HA)集群搭建