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服务器上的临时有序节点,此时删除节点触发了监听的回调函数,下一个线程获取了锁。这样就完成了分布式锁的所有逻辑