zookeeper应用场景之分布式锁

1)引入相关依赖


<dependency>
<groupId>com.shentu</groupId>
<artifactId>zookeeper_loadbalance_api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<!--解决slf4j-log4j12-->
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>

<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.jetbrains/annotations -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
</dependency>

2)创建DistributedLock.java类

因为该类需要实现分布式锁的功能,因此需要实现 Lock 和 Watcher 接口并重写其抽象方法

因为分布式锁原理比较复杂,这里再次引入其原理图:



@Override
public boolean tryLock() {
try {
CURRENT_LOCK = zk.create(ROOT_LOCK + "/", "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
log.info(Thread.currentThread().getName() + "-->" + CURRENT_LOCK + "|尝试竞争锁!");
List<String> children = zk.getChildren(ROOT_LOCK, false);
SortedSet<String> sortedSet = new TreeSet<>();
children.forEach(child -> {
sortedSet.add(ROOT_LOCK + "/" + child);
});
String firstNode = sortedSet.first();
if (StringUtils.equals(firstNode, CURRENT_LOCK)) {
return true;
}
SortedSet<String> lessThenMe = sortedSet.headSet(CURRENT_LOCK);
if (CollectionUtils.isNotEmpty(lessThenMe)) {
WAIT_LOCK = lessThenMe.last();
}
return false;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

该类中的核心方法是tryLock()方法,zookeeper连接首先去创建临时有序节点,创建完成之后会去获取根节点下的所有子节点,对子节点进行排序,如果当前
节点就是最小的子节点,那么他获取锁成功并返回自己的节点,如果当前节点不是最小节点,那么它将返回他的前一个节点。

3)创建测试类进行测试

public class Test1 {

public static void main(String[] args) throws IOException {
for (int j = 0; j < 10; j++) {

CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
try {
countDownLatch.await();
DistributedLock lock = new DistributedLock();
lock.lock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread<" + i + ">").start();
countDownLatch.countDown();
}
System.in.read();
}
}
}
该测试类首先使用线程池模拟共享访问的场景,待10个线程开启完成后,会创建10个zookeeper连接对象。接下来启动lock方法获取锁,成功获取锁的线程执行业务逻辑,
执行完成后会自动删除zookeeper服务器上的临时有序节点,此时删除节点触发了监听的回调函数,下一个线程获取了锁。这样就完成了分布式锁的所有逻辑

4)代码测试的结果如下:

启动应用,10个线程分别初始化完毕并且开始竞争锁

 

竞争的结果显示0000000120线程获得锁成功,其他线程均需要等待锁释放

使用zkCli工具查看节点,其中/lock就是管理分布式锁的根节点

查询其子节点可知它包含了10个子节点,此时我们手动删除0000000120子节点,用来模拟业务执行完毕或者宕机下线的情景

 

从控制台的日志可知,0000000120下线之后,触发了0000000121的监听器回调函数,0000000121线程顺利获取了锁并开始执行自己的业务逻辑,这样就完成了分布式锁的全部逻辑

 
posted @ 2020-01-06 13:09  不缺重头再来的勇气  阅读(289)  评论(0编辑  收藏  举报