Zookeeper是一个分布式管理框架,本文仅展示分布式锁代码
Zookeeper 是一个开源的分布式的,为分布式框架提供协调服务的 Apache 项目。是一个基
于观察者模式设计的分布式服务管理框架。
代码如下(示例):
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-coreartifactId>
<version>2.8.2version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.5.7version>
dependency>
代码如下(示例):
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
代码如下(示例):
package com.demo.zk.lock;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* @author 小肆2019
* @date 2023/3/25/025
* @desc to do something
* @since 1.0
*/
public class ZkLock {
private final String connectString = "110.40.194.181:2181";
private final int sessionTimeout = 2000;
private ZooKeeper zk;
private String rootNode = "locks";
private String subNode = "seq-";
// 当前 client 等待的子节点
private String waitPath;
//ZooKeeper 连接
private CountDownLatch connectLatch = new CountDownLatch(1);
//ZooKeeper 节点等待
private CountDownLatch waitLatch = new CountDownLatch(1);
// 当前 client 创建的子节点
private String currentNode;
public ZkLock() throws Exception {
zk = new ZooKeeper(connectString, sessionTimeout, watchedEvent -> {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
connectLatch.countDown();
}
if (watchedEvent.getType() == Watcher.Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)) {
waitLatch.countDown();
}
});
// 等待连接建立
connectLatch.await();
// 获取根节点状态
Stat stat = zk.exists("/" + rootNode, false);
// 如果根节点不存在,则创建根节点,根节点类型为永久节点
if (stat == null) {
System.out.println("rootNode not exist");
zk.create("/" + rootNode, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
// 加锁
public void zkLock() {
try {
currentNode = zk.create(
"/" + rootNode + "/" + subNode,
null,
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
Thread.sleep(10);
// 注意, 没有必要监听"/locks"的子节点的变化情况
List<String> children = zk.getChildren("/" + rootNode, false);
// 列表中只有一个子节点,那肯定就是currentNode。说明client获取锁
if (children.size() == 1) {
return;
} else {
// 对根节点下的所有临时顺序节点进行从小到大排序
Collections.sort(children);
String thisNode = currentNode.substring(("/" + rootNode + "/").length());
// 获取当前节点的位置
int index = children.indexOf(thisNode);
if (index == -1) {
System.out.println("data error");
} else if (index == 0) {
// index == 0, 说明 thisNode 在列表中最小, 当前client 获得锁
return;
} else {
// 获得排名比 currentNode 前1位的节点
this.waitPath = "/" + rootNode + "/" + children.get(index - 1);
// 在waitPath上注册监听器,当waitPath删除时,zookeeper会回调监听器的方法
zk.getData(waitPath,true,new Stat());
// 进入等待锁状态
waitLatch.await();
return;
}
}
} catch (KeeperException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 解锁
public void zkUnlock(){
try {
zk.delete(this.currentNode,-1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (KeeperException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
ZkLock lock1 = new ZkLock();
ZkLock lock2 = new ZkLock();
new Thread(() -> {
lock1.zkLock();
System.out.println("线程 1 获取锁");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
lock1.zkUnlock();
System.out.println("线程 1 释放锁");
}).start();
new Thread(() -> {
lock2.zkLock();
System.out.println("线程 2 获取锁");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
lock2.zkUnlock();
System.out.println("线程 2 释放锁");
}).start();
}
}
说明:java代码实现方式是利用的CountDownLatch锁的特性,当CountDownLatch锁的state值不为0就会一直进行阻塞。当我本次创建的节点按照从小到大的顺序排列不是第一个节点,就说明没有抢到锁,于是监听上一个节点,等到上一个节点被删除(释放锁),再获取锁。
代码如下(示例):
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>4.3.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>4.3.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-clientartifactId>
<version>4.3.0version>
dependency>
代码如下(示例):
package com.demo.zk.curator;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
/**
* @author wo595
* @date 2023/3/25/025
* @desc to do something
* @since 1.0
*/
public class CuratorLock {
private String rootNode = "/locks";
private String connectString = "110.40.194.181:2181";
private int sessionTimeout = 20000;
private int connectionTimeout = 20000;
public static void main(String[] args) {
new CuratorLock().test();
}
public void test() {
final InterProcessLock lock1 = new InterProcessMutex(getCuratorFramework(), rootNode);
final InterProcessLock lock2 = new InterProcessMutex(getCuratorFramework(), rootNode);
new Thread(() -> {
try {
// 获取锁对象
lock1.acquire();
System.out.println("thread1 acquire lock");
// 测试锁的重入
lock1.acquire();
System.out.println("thread1 acquire lock again");
Thread.sleep(5000);
lock1.release();
System.out.println("thread1 release lock");
lock1.release();
System.out.println("thread1 release lock again");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
// 获取锁对象
lock2.acquire();
System.out.println("thread2 acquire lock");
// 测试锁的重入
lock2.acquire();
System.out.println("thread2 acquire lock again");
Thread.sleep(5000);
lock2.release();
System.out.println("thread2 release lock");
lock2.release();
System.out.println("thread2 release lock again");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
private CuratorFramework getCuratorFramework() {
RetryPolicy policy = new ExponentialBackoffRetry(3000, 3);
CuratorFramework build = CuratorFrameworkFactory.builder()
.connectString(connectString)
.connectionTimeoutMs(connectionTimeout)
.sessionTimeoutMs(sessionTimeout)
.retryPolicy(policy)
.build();
build.start();
System.out.println("init zookeeper success");
return build;
}
}
使用curator。