ZooKeeper实现分布式锁
·
1.实现原理
Zookeeper节点每个只能创建一个,多个创建会失败,临时节点每次关闭连接都会失效,获取锁则对应为创建临时节点,释放锁则关闭连接。,多JVM获取锁通过监听事件通知来获取锁
2. 实现代码
引入依赖
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
1.伪订单服务
OrderNumGenerator
package com.zou;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author WH
* @version 1.0
* @date 2020/2/15 10:01
*/
public class OrderNumGenerator {
//生成订单号
private static int count = 0;
public String getNumber() {
try {
Thread.sleep(200);
} catch (Exception e) {
}
SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return simpt.format(new Date()) + "-" + ++count;
}
}
- 锁接口
ZKLock
public interface ZKLock {
//获取锁
public void getLock();
//释放锁
public void unLock();
}
- 抽象类
ZooKeeperAbstractLock
package com.zou;
import org.I0Itec.zkclient.ZkClient;
import java.util.concurrent.CountDownLatch;
/**
* 模板方法设计模式抽离重复代码到子类
* @author WH
* @version 1.0
* @date 2020/2/15 9:26
*/
public abstract class ZooKeeperAbstractLock implements ZKLock {
private static final String CONNECTION = "49.233.150.105:2181";
protected ZkClient zkClient = new ZkClient(CONNECTION);
protected String lockPath = "/lockPath";
protected CountDownLatch countDownLatch = null;
/*
* 获取锁
* */
@Override
public void getLock() {
//1. 连接zk,在zk上创建/lock节点,节点为临时节点
//2. 如果节点创建成功,获取锁,否则等待
if (tryLock()) {
System.out.println("获取锁成功");
} else {
//3. 事件监听节点是否被删除,删除则重新获取锁资源
waitLock();
// 4.重新获取锁
getLock();
}
}
/**
* 获取锁资源,成功返回true,否则false
*/
abstract void waitLock();
abstract boolean tryLock();
/*
* 释放锁
* */
@Override
public void unLock() {
//程序执行完毕关闭连接
if (zkClient != null) {
zkClient.close();
System.out.println("释放锁成功");
}
}
}
- ZookeeperDistrbuteLock
package com.zou;
import org.I0Itec.zkclient.IZkDataListener;
import java.util.concurrent.CountDownLatch;
/**
* @author WH
* @version 1.0
* @date 2020/2/15 9:38
*/
public class ZookeeperDistrbuteLock extends ZooKeeperAbstractLock {
@Override
void waitLock() {
//监听事件通知
IZkDataListener iZkDataListener = new IZkDataListener() {
//节点修改
@Override
public void handleDataChange(String s, Object o) throws Exception {
}
//节点删除
@Override
public void handleDataDeleted(String s) throws Exception {
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
};
//注册事件
zkClient.subscribeDataChanges(lockPath, iZkDataListener);
// 如果节点存在
if (zkClient.exists(lockPath)) {
try {
countDownLatch = new CountDownLatch(1);
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
//删除事件 防止事件监听多次
zkClient.unsubscribeDataChanges(lockPath, iZkDataListener);
}
@Override
boolean tryLock() {
try {
//创建临时节点
zkClient.createEphemeral(lockPath);
return true;
} catch (Exception e) {
//如果临时节点创建失败
return false;
}
}
}
- 测试类
OrderService
package com.zou;
/**
* @author WH
* @version 1.0
* @date 2020/2/15 9:58
*/
public class OrderService {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
private ZKLock zkLock = new ZookeeperDistrbuteLock();
//获取订单号
public void getNumber() {
try {
zkLock.getLock();
String number = orderNumGenerator.getNumber();
System.out.println("线程:" + Thread.currentThread().getName() + "生成订单号为:" + number);
} catch (Exception e) {
} finally {
zkLock.unLock();
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() ->{
//模拟多个JVM产生多个OrderService实例
System.out.println("线程:" + Thread.currentThread().getName() + "启动");
new OrderService().getNumber();
}).start();
}
}
}
实现效果

更多推荐
所有评论(0)