分布式锁-基于Zookeeper实现

分布式锁-基于Zookeeper实现


1. Zookeeper安装部署

1.1 安装部署参考文档

分布式注册中心-Zookeeper: https://github.com/FocusProgram/person-improve/tree/main/springcloud-zookeeper

1.2 Zookeeper客户端工具

下载地址:https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

2. 实现原理

  • 多个JVM同时在根节点下创建临时节点(/lock)
  • 创建临时节点(/lock)成功,则获取锁成功,执行响应的逻辑代码
  • 创建临时节点(/lock)失败,则获取锁失败,通过订阅节点删除通知事件,如果节点删除则重新获取锁,否则就一直等待
  • 执行响应业务逻辑完成后关闭连接,临时节点删除,锁释放

3. 具体实现

3.1 引入Gradle依赖

1
2
3
dependencies {
compile group: 'com.101tec', name: 'zkclient', version: '0.11'
}

3.2 具体业务实现

3.2.1 创建CustomLock接口

1
2
3
4
5
6
7
8
9
10
11
12
public interface CustomLock {

/**
* 获取锁
*/
public void getLock();

/**
* 释放锁
*/
public void unLock();
}

3.2.2 创建ZookeeperAbstractLock抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public abstract class ZookeeperAbstractLock implements CustomLock {

// zk连接地址
private String CONNECTSTRING = "114.55.34.44:2181";

// 创建zk连接
protected ZkClient zkClient = new ZkClient(CONNECTSTRING);

// zk节点创建路径目录
protected String PATH = "/lock";

// 通过定义计数器标识创建临时节点状态
protected CountDownLatch countDownLatch = new CountDownLatch(1);

/**
* 获取锁
*/
@Override
public void getLock() {
if (tryLock()) {
System.out.println("获取lock锁的资源");
} else {
// 等待
waitLock();
// 重新获取锁资源
getLock();
}

}

/**
* 释放锁
*/
@Override
public void unLock() {
if (zkClient != null) {
zkClient.close();
System.out.println("释放lock锁资源");
}
}

protected abstract void waitLock();

protected abstract boolean tryLock();
}

3.2.3 创建ZookeeperDistrbuteLock抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {

@Override
protected void waitLock() {

IZkDataListener iZkDataListener = new IZkDataListener() {
/**
* 节点发生改变时事件通知
* @param dataPath
* @param data
* @throws Exception
*/
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {

}

/**
* 节点删除是事件通知
* @param dataPath
* @throws Exception
*/
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("删除的节点路径为" + dataPath);
// 唤醒等待的线程
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
};

//注册监听事件
zkClient.subscribeDataChanges(PATH, iZkDataListener);

if (zkClient.exists(PATH)) {
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}

//删除监听事件
zkClient.unsubscribeDataChanges(PATH, iZkDataListener);

}

@Override
protected boolean tryLock() {
try {
zkClient.createEphemeral(PATH);
System.out.println("获取lock锁成功");
return true;
} catch (Exception e) {
System.out.println("获取lock锁失败");
return false;
}
}
}

3.2.4 使用Zookeeper实现分布式锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class NumberGenerator {

// 生成订单号规则
private static int count = 0;

public String getNumber() {
// try {
// Thread.sleep(200);
// } catch (Exception e) {
// e.printStackTrace();
// }
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return simpleDateFormat.format(new Date()) + "-" + ++count;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class OrderService implements Runnable {

private NumberGenerator numberGenerator = new NumberGenerator();

private CustomLock customLock = new ZookeeperDistrbuteLock();

@Override
public void run() {
getNumber();
}

public void getNumber() {
try {
customLock.getLock();
String number = numberGenerator.getNumber();
System.out.println(Thread.currentThread().getName() + "生成订单号:" + number);
} catch (Exception e) {
e.printStackTrace();
} finally {
customLock.unLock();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class LockTest {

/**
* 根据cpu的数量动态的配置核心线程数和最大线程数
*/
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/**
* 核心线程数 = CPU核心数 + 1
*/
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
/**
* 线程池最大线程数 = CPU核心数 * 2 + 1
*/
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
/**
* 非核心线程闲置时超时1s
*/
private static final int KEEP_ALIVE = 1;

public static void main(String[] args) {

System.out.println("开始生成订单号......");
long begin = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
new Thread(new OrderService()).start();
}
long end = System.currentTimeMillis();
long result = end - begin;
System.out.println("执行消耗时长:" + result / 1000 + "s");
}
}

3.2.5 源码参考地址

https://github.com/FocusProgram/person-improve/tree/main/springcloud-zookeeper-lock


分布式锁-基于Zookeeper实现
http://yoursite.com/2020/07/11/分布式锁-基于Zookeeper实现/
作者
Mr.Kong
发布于
2020年7月11日
许可协议