zookeeper的原理分析参考链接:https://blog.csdn.net/gelong_bokewang/article/details/89208234
zookeeper的安装步骤参考链接:https://blog.csdn.net/gelong_bokewang/article/details/89157375
这里我们需要引入的jar包:
org.apache.zookeeper
zookeeper
3.4.14
org.apache.curator
curator-recipes
4.1.0
org.apache.curator
curator-client
4.1.0
锁原理:
1、客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。
2、想要获取锁的客户端在锁的根节点下面创建znode,作为/mylock的子节点,节点的类型要选择CreateMode.PERSISTENT_SEQUENTIAL,,假设目前同时有3个客户端想要获得锁,那么/mylock下的目录应该是这个样子的。
xxx-lock-0000000001,xxx-lock-0000000002,xxx-lock-0000000003
xxx为uuid , 0000000001,0000000002,0000000003 是zook服务端自动生成的自增数字。
3、当前客户端通过getChildren(/mylock)获取所有子节点列表并根据自增数字排序,然后判断一下自己创建的节点的顺序是不是在列表当中最小的,如果是 那么获取到锁,如果不是,那么获取自己的前一个节点,并设置监听这个节点的变化,当节点变化时重新执行步骤3 直到自己是编号最小的一个为止。
举例:假设当前客户端创建的节点是0000000002,因为它的编号不是最小的,所以获取不到锁,那么它就找到它前面的一个节点0000000001 并对它设置监听。
4、释放锁,当前获得锁的客户端在操作完成后删除自己创建的节点,这样会激发zook的事件给其它客户端知道,这样其它客户端会重新执行(步骤3)。
举例:加入客户端0000000001获取到锁,然后客户端0000000002加入进来获取锁,发现自己不是编号最小的,那么它会监听它前面节点的事件(0000000001的事件)然后执行步骤(3),当客户端0000000001操作完成后删除自己的节点,这时zook服务端会发送事件,这时客户端0000000002会接收到该事件,然后重复步骤3直到获取到锁)
工具类:这里Curator是一个重量级对象,最好唯一,统一初始化。
package src.main.javas.com.gl.zookeeper;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
/**
* @ ZookeeperUtil
* 因为Curator客户端是重量级对象,只需要初始化一次
*/
public class ZookeeperUtil {
/**
* 超时重试的时间
*/
private static final Integer baseSleepTimeMs = 1000;
/**
* 超时重试的次数
*/
private static final Integer maxRetries = 3;
/**
* zookeeper服务器的地址端口
*/
private static final String connectString = "192.168.80.112:2181,192.168.80.112:2182,192.168.80.112:2183";
/**
* 操作失败重试机制
*/
private RetryPolicy retryPolicy;
/**
* Curator客户端
*/
private CuratorFramework curatorFramework;
/**
* 初始化
*/
public ZookeeperUtil() {
retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries);
curatorFramework = CuratorFrameworkFactory.newClient(connectString,retryPolicy);
curatorFramework.start();
}
public CuratorFramework getCuratorFramework() {
return curatorFramework;
}
public void close() {
curatorFramework.close();
}
}
实现代码:
package src.main.javas.com.gl.zookeeper;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ZookeeperDemo1 {
public static void main(String[] args) throws Exception {
ZookeeperUtil zookeeperUtil = new ZookeeperUtil();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,10,10000, TimeUnit.SECONDS,
new ArrayBlockingQueue(1),
new ThreadPoolExecutor.DiscardOldestPolicy());
test(zookeeperUtil, threadPoolExecutor);
//关闭
//zookeeperUtil.close();
}
public static void test(ZookeeperUtil zookeeperUtil, ThreadPoolExecutor threadPoolExecutor) {
threadPoolExecutor.execute(() ->{
try {
/**
* 这个类是线程安全的,一个JVM创建一个就好
* mylock 为锁的根目录,我们可以针对不同的业务创建不同的根目录
*/
final InterProcessMutex lock = new InterProcessMutex(zookeeperUtil.getCuratorFramework(), "/mylock");
try {
//阻塞方法,获取不到锁线程会挂起。
lock.acquire();
System.out.println("已经获取到锁1");
} catch (Exception e) {
e.printStackTrace();
}
finally{
//释放锁,必须要放到finally里面,已确保上面方法出现异常时也能够释放锁。
Thread.sleep(3000);
lock.release();
}
}catch (Exception e) {
e.printStackTrace();
}finally {
threadPoolExecutor.shutdown();
}
});
threadPoolExecutor.execute(() -> {
try {
/**
* 这个类是线程安全的,一个JVM创建一个就好
* mylock 为锁的根目录,我们可以针对不同的业务创建不同的根目录
*/
final InterProcessMutex lock = new InterProcessMutex(zookeeperUtil.getCuratorFramework(), "/mylock");
try {
//阻塞方法,获取不到锁线程会挂起。
lock.acquire();
System.out.println("已经获取到锁2");
} catch (Exception e) {
e.printStackTrace();
}
finally{
Thread.sleep(5000);
//释放锁,必须要放到finally里面,已确保上面方法出现异常时也能够释放锁。
lock.release();
}
}catch (Exception e) {
e.printStackTrace();
}finally {
threadPoolExecutor.shutdown();
}
});
threadPoolExecutor.execute(() -> {
try {
/**
* 这个类是线程安全的,一个JVM创建一个就好
* mylock 为锁的根目录,我们可以针对不同的业务创建不同的根目录
*/
final InterProcessMutex lock = new InterProcessMutex(zookeeperUtil.getCuratorFramework(), "/mylock");
try {
//阻塞方法,获取不到锁线程会挂起。
lock.acquire();
System.out.println("已经获取到锁3");
} catch (Exception e) {
e.printStackTrace();
}
finally{
Thread.sleep(8000);
//释放锁,必须要放到finally里面,已确保上面方法出现异常时也能够释放锁。
lock.release();
}
}catch (Exception e) {
e.printStackTrace();
}finally {
threadPoolExecutor.shutdown();
}
});
}
}
运行上述的代码时候我们可以在zookeeper服务器上进行查询可以看到如下结果:
这样的输出我们明显的能看明白zookeeper分布式锁的原理,他是一个先进先出的机制,而且这里我们明显能够看到的它获取锁的方式。