Zookeeper案例总结目录
- 1. 服务器动态上下线监听案例
- 2. ZooKeeper 分布式锁案例
- 3.Curator 框架实现分布式锁案例
1. 服务器动态上下线监听案例
- 需求
- 某分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线。
- 需求分析
- 简单描述就是实现集群可以存储服务器的节点信息,客户端实现监听节点变化的功能
- 实现过程
- 首先在集群上登录客户端创建一个节点: create /servers “servers”
- 在IDEA中实现以下代码:
DistributeClient.java
public class DistributeClient {
private ZooKeeper zooKeeper;
private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private int sessionTimeout = 2000000;
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
DistributeClient client = new DistributeClient();
//1.获取zookeeper连接
client.getConnect();
//2.监听/servers下面节点的删除和添加
client.getServerList();
//3.执行业务逻辑
client.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void getServerList() throws InterruptedException, KeeperException {
//获取/servers路径下的所有节点
List<String> children = zooKeeper.getChildren("/servers", true);
//保存节点数据
ArrayList<String> server = new ArrayList<>();
for (String child : children) {
//获取节点数据
byte[] data = zooKeeper.getData("/servers/" + child, false, null);
//将节点数据添加到集合
server.add(new String(data));
}
System.out.println(server);
}
private void getConnect() throws IOException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
getServerList();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
});
}
}
DistributeServer.java
public class DistributeServer {
private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private int sessionTimeout = 20000000;
private ZooKeeper zooKeeper;
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
DistributeServer server = new DistributeServer();
//1.获取zk集群连接
server.getConnect();
//2.注册服务器到集群
server.register(args[0]);
//3.执行业务逻辑
server.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void register(String hostname) throws InterruptedException, KeeperException {
//2.注册服务器到集群
String create = zooKeeper.create("/servers/"+hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + "is online");
}
private void getConnect() throws IOException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
}
});
}
}
2. ZooKeeper 分布式锁案例
- 分布式锁的概念:
- 比如说"进程 1"在使用该资源的时候,会先去获得锁,"进程 1"获得锁以后会对该资源保持独占,这样其他进程就无法访问该资源,"进程 1"用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。
- 案例分析
- 过程描述:多个客户端都让服务器创建临时节点来处理业务,为保证在分布式环境下,单个进程依次执行业务操作,所以需要上锁,具体实现步骤如下:
- ①接收到请求后,在/locks节点下创建一个临时顺序节点
- ②判断自己是不是当前节点下最小的节点:是,获取到锁;不是,对前一个节点进行监听
- ③获取到锁,处理完业务后,delete节点释放锁,然后下面的节点将收到通知,重复第二步判断
DistributeLock.java
public class DistributeLock {
private final String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private final int sessionTimeout = 2000000;
private final ZooKeeper zooKeeper;
//等待上一个线程结束之后才会执行后面的代码
private CountDownLatch connectLatch = new CountDownLatch(1);
private CountDownLatch waitLatch = new CountDownLatch(1);
private String waitPath;
private String currentMode;
public DistributeLock() throws IOException, InterruptedException, KeeperException {
//获取连接
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
//如果连接上connectLatch需要释放
if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
connectLatch.countDown();
}
//如果是删除节点并且还是前一个节点路径就释放
if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)){
waitLatch.countDown();
}
}
});
connectLatch.await();
//判断根节点“/locks”是否存在
Stat stat = zooKeeper.exists("/locks", false);
//如果根节点不存在就创建根节点
if (stat == null){
String rootNode = zooKeeper.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
//上锁
public void lock() {
//创建临时的带序号的节点
try {
currentMode = zooKeeper.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
//判断创建的节点是不是序号最小的,如果是就获取锁,如果不是就释放锁
List<String> children = zooKeeper.getChildren("/locks", false);
if (children.size() == 1){//只有一个节点表明现在的节点就是最小的
return;
}else {//需要判断当前节点所在位置
//对集合中的
Collections.sort(children);
//获取当前节点序号seq-000000000001
String thisNode = currentMode.substring("/locks/".length());
int index = children.indexOf(thisNode);
//判断当前节点状态
if (index == -1 ){
System.out.println("数据异常");
}else if (index == 0){
return;
}else {
//需要监听前一个节点
waitPath = "/locks/"+children.get(index - 1);
zooKeeper.getData(waitPath,true,new Stat());
waitLatch.await();
return;
}
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//解锁
public void unlock(){
try {
zooKeeper.delete(currentMode,1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
}
DistributeLockTest.java
public class DistributeLockTest {
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
final DistributeLock lock1 = new DistributeLock();
final DistributeLock lock2 = new DistributeLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.lock();
System.out.println("线程1启动,获取到锁");
Thread.sleep(6*1000);
System.out.println("线程1释放锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.lock();
System.out.println("线程2启动,获取到锁");
Thread.sleep(6*1000);
System.out.println("线程2释放锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
3.Curator 框架实现分布式锁案例
- 原生的 Java API 开发存在的问题
- (1)会话连接是异步的,需要自己去处理。比如使用 CountDownLatch
- (2)Watch 需要重复注册,不然就不能生效
- (3)开发的复杂性比较高的
- (4)不支持多节点删除和创建。需要自己去递归
- Curator 是一个专门解决分布式锁的框架,解决了原生 JavaAPI 开发分布式遇到的问题。
Curator实现分布式锁
public class CuratorLockTest {
public static void main(String[] args) {
//创建分布式锁1
InterProcessMutex lock1 = new InterProcessMutex(getCuratorFrameWork(), "/locks");
//创建分布式锁2
InterProcessMutex lock2 = new InterProcessMutex(getCuratorFrameWork(), "/locks");
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.acquire();
System.out.println("线程1获取到锁");
lock1.acquire();
System.out.println("线程1再次获取到锁");
lock1.release();
System.out.println("线程1释放锁");
lock1.release();
System.out.println("线程1再次释放锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.acquire();
System.out.println("线程2获取到锁");
lock2.acquire();
System.out.println("线程2再次获取到锁");
lock2.release();
System.out.println("线程2释放锁");
lock2.release();
System.out.println("线程2再次释放锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private static CuratorFramework getCuratorFrameWork() {
//指数退避重试
ExponentialBackoffRetry policy = new ExponentialBackoffRetry(30000, 3);
//通过Curator框架工厂创建客户端对象
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("hadoop102:2181,hadoop103:2181,hadoop104:2181")
.connectionTimeoutMs(200000)
.sessionTimeoutMs(2000000)
.retryPolicy(policy).build();
//开启客户端
client.start();
System.out.println("客户端启动成功");
return client;
}
}