ZooKeeper入门(二)Java API开发实战
上篇文章ZooKeeper入门(一)应用场景与安装搭建 - 肥兔子爱豆畜子 - 博客园 (cnblogs.com)介绍了ZooKeeper的应用场景也就是能干什么,并且搭建了单机和1主2从的集群环境。这次我们来使用ZooKeeper自带的Java客户端API实现一个简单的分布式锁。
gradle依赖:implementation group: 'org.apache.zookeeper', name: 'zookeeper', version: '3.6.3'
先来看一个场景:
@Slf4j
public class ExclusiveLockTest {
private static int num = 0;
public static void main(String[] args) {
List<Thread> threads = new ArrayList<>();
for(int i=0; i<10; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0; i<100; i++) {
num++;
}
}});
t.start();
threads.add(t);
}
for(Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("num is {}", num);
}
}
上面的代码显然是有问题的,启10个线程分别对num进行累加10次的操作,由于线程间未使用任何同步机制,最后运行的结果几次都不一样,不是期望的1000 。
一个简单的ZooKeeper分布式锁
由于是在一个JVM进程内显然是可以使用Java的各种锁来进行同步的,但这里我们使用进程外的ZooKeeper来做协调、使用分布式锁来同步这10个线程。
设计思路:
1、ZkDistributeLock
这个类提供了分布式锁的功能,提供了两个方法accquiredLock()
和releaseLock()
。
2、分布式锁本质上就是/exclusive_lock/locked
这样一个临时节点,线程创建这个节点成功则代表抢锁成功,执行临界区代码结束后删除这个临时节点,代表释放锁。
3、线程抢锁过程中尝试创建该节点,如果发现该临时节点已存在、则使用exists(nodePath, watcher)
再次判断节点是否存在,同时绑定watcher到这个节点:
- 如果这次该节点不存在、说明之前持有锁的线程刚好此时删除了锁节点、那么就再次
accquiredLock()
尝试获得锁。 - 如果节点仍存在且watcher也绑定到该节点了,则线程阻塞等待在watcher对象上。锁节点被删除时ZooKeeper会通知watcher对象,执行process回调方法进行notify、唤醒阻塞等待的线程重新
accquiredLock()
抢锁。
代码如下:
/**
* ZooKeeper分布式锁
* */
@Slf4j
public class ZkDistributeLock {
private ZooKeeper client;
private Watcher watcher;
private CountDownLatch connectedSignal = new CountDownLatch(1);
public ZkDistributeLock(String zkAddress) {
try {
ZKClientConfig config = new ZKClientConfig();
config.setProperty("zookeeper.sasl.client", "false");
client = new ZooKeeper(zkAddress, 30000, new Watcher() {
@Override
public void process(WatchedEvent event) {
Event.KeeperState state = event.getState();
// 连接成功
if (state == Event.KeeperState.SyncConnected) {
log.info("与ZooKeeper服务器连接成功");
connectedSignal.countDown();
}
// 断开连接
else if (state == Event.KeeperState.Disconnected){
log.info("与ZooKeeper服务器断开连接");
}
}
}, true, config);
connectedSignal.await(); //等待连接创建完成
watcher = new Watcher() { //实例化watcher,监听锁节点被删除则唤醒其他线程抢锁
@Override
public void process(WatchedEvent event) {
synchronized (this) {
notifyAll();
}
}
};
} catch (Exception e) {
e.printStackTrace();
}
}
//获取锁
public void accquiredLock() throws Exception {
String path = null;
try {
path = client.create("/exclusive_lock/locked", Thread.currentThread().getName().getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
log.info("线程{}获得锁", Thread.currentThread().getName());
}catch(KeeperException.NodeExistsException nodeExists) {
log.info("线程{}未获得锁", Thread.currentThread().getName());
Stat stat = client.exists("/exclusive_lock/locked", this.watcher);
if (stat == null) { //节点没了、说明刚才这会儿抢到锁的连接断开了或者释放了锁,那就直接抢锁
log.info("线程{}未获得锁,未设置watcher,尝试直接枪锁", Thread.currentThread().getName());
accquiredLock();
} else { //查到了节点并且设置好了watcher,那就阻塞在watcher上、等待watcher收到通知后进行notify
log.info("线程{}未获得锁,已设置watcher,阻塞等待watcher收到通知后进行notify", Thread.currentThread().getName());
synchronized (watcher) {
watcher.wait();
accquiredLock();
}
}
}
}
//释放锁
public void releaseLock() throws Exception{
client.delete("/exclusive_lock/locked", -1);
//client.close();
log.info("线程{}释放锁", Thread.currentThread().getName());
}
}
使用ZkDistributeLock简单分布式锁
修改后的测试程序,使用了上述分布式锁:
package com.wangan.zookeeper.xlock;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ExclusiveLockTest {
private static int num = 0;
private static String zkAddress = "122.xx.xxx.187:2181";
public static void main(String[] args) {
List<Thread> threads = new ArrayList<>();
for(int i=0; i<10; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
ZkDistributeLock lock = new ZkDistributeLock(zkAddress);
try {
lock.accquiredLock();
} catch (Exception e) {
log.error("获取分布式锁失败:" + e.getMessage(), e);
}
for(int i=0; i<100; i++) {
num++;
}
try {
lock.releaseLock();
} catch (Exception e) {
log.error("释放分布式锁失败:" + e.getMessage(), e);
}
}
});
t.start();
threads.add(t);
}
for(Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("num is {}", num);
}
}
执行结果:
[2022-02-09 14:56:41] [ INFO ] [Thread-3-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-10-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-2-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-9-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-4-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-5-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-1-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-8-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-7-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-6-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1181] - Opening socket connection to server /122.51.112.187:2181.
[2022-02-09 14:56:41] [ INFO ] [Thread-5-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54990, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-3-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54988, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-8-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54993, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-4-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54985, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-10-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54991, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-7-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54987, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-2-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54989, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-6-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54984, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-1-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54992, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-9-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1013] - Socket connection established, initiating session, client: /10.100.156.92:54986, server: /122.51.112.187:2181
[2022-02-09 14:56:41] [ INFO ] [Thread-10-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c0031, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-4-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c0032, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-6-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c002d, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-7-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c002c, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-2-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c002e, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-5-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c002f, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-3-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c0030, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-4-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-10-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-7-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-5-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-2-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-3-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-6-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-8-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c0034, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-9-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c0035, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-1-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1448] - Session establishment complete on server /122.51.112.187:2181, session id = 0x110e515ec2c0033, negotiated timeout = 30000
[2022-02-09 14:56:41] [ INFO ] [Thread-9-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-1-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-8-EventThread] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:43] - 与ZooKeeper服务器连接成功
[2022-02-09 14:56:41] [ INFO ] [Thread-7] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-7获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-5] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-5未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-3未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-6] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-6未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-8未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-2未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-10未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-1未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-4未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-7] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-7释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-5] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-5未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-10未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-6] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-6未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-3未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-8未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-2未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-4未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-1未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-2未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-8未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-3未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-10未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-5] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-5未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-4未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-1未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-6] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-6获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-3未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-10未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-5] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-5未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-8未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-4未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-6] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-6释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-2未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-1未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-8未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-4未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-10未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-3未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-2未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-1未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-5] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-5获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-5] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-5释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-3未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-10未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-8未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-2未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-1未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-4未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-3未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-10未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-2获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-8未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-1未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-4未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-3未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-10未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-4未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-1未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-8未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-2] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-2释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-3获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-10未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-8未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-1未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-4未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-10未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-3] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-3释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-8未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-1未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-4未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-10获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-10] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-10释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-8未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-1获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-4未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:91] - 线程Thread-8未获得锁,已设置watcher,阻塞等待watcher收到通知后进行notify
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-4未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-1] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-1释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-8获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-4未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-8] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-8释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:91] - 线程Thread-4未获得锁,已设置watcher,阻塞等待watcher收到通知后进行notify
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-4获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:83] - 线程Thread-9未获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:88] - 线程Thread-9未获得锁,未设置watcher,尝试直接枪锁
[2022-02-09 14:56:41] [ INFO ] [Thread-4] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-4释放锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:79] - 线程Thread-9获得锁
[2022-02-09 14:56:41] [ INFO ] [Thread-9] [traceId:] com.wangan.zookeeper.xlock.ZkDistributeLock [codeline:106] - 线程Thread-9释放锁
[2022-02-09 14:56:41] [ INFO ] [main] [traceId:] com.wangan.zookeeper.xlock.ExclusiveLockTest [codeline:52] - num is 1000
遇到的问题
1、客户端连接建立比较慢,需要1、20秒
在网上找到了原因记一次zookeeper连接慢的问题和解决方法_随便写写-CSDN博客,是启用ZooKeeperSaslClient之后查DNS导致的,这篇文章里是在本地hosts文件配置解决的,而笔者是在创建连接的时候直接关闭了这个配置:
ZKClientConfig config = new ZKClientConfig();
config.setProperty("zookeeper.sasl.client", "false");
client = new ZooKeeper(zkAddress, 30000, new Watcher(), true, config);
2、wait/notify死锁问题
上面代码中没抢到锁的线程会对锁节点进行watch并wait在watcher这个对象上,阻塞等待watcher收到ZooKeeper通知然后进行notifyAll()唤醒。但wait、notify机制需要先wait再notify,如果是先notify的话wait是会死锁的。
比如上面的代码中,执行accquiredLock()的线程假设为t1,其未抢到锁、执行了client.exists("/exclusive_lock/locked", this.watcher)
并成功绑定了watcher到锁节点,而这时候持锁线程马上先删除了锁节点触发通知watcher的process方法执行notifyAll(),而这之后t1线程才刚走到watcher.wait()
这步,这样就触发了死锁。
3、连接被服务端关闭的问题
在释放锁的逻辑中,思路是删除/exclusive_lock/locked
节点然后close关闭连接:
//释放锁
public void releaseLock() throws Exception{
client.delete("/exclusive_lock/locked", -1);
client.close();
log.info("线程{}释放锁", Thread.currentThread().getName());
}
但是在调用client.close()这步时,经常会报如下WARN:
[2022-02-09 15:44:49] [ WARN ] [Thread-8-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1294] - An exception was thrown while closing send thread for session 0x110e515ec2c003b.
org.apache.zookeeper.ClientCnxn$EndOfStreamException: Unable to read additional data from server sessionid 0x110e515ec2c003b, likely server has closed socket
at org.apache.zookeeper.ClientCnxnSocketNIO.doIO(ClientCnxnSocketNIO.java:77)
at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:350)
at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1290)
不过好像没有影响到最终执行的结果,仍然是num=1000
这个问题困扰了笔者一天,跟多线程没有关系,可以用如下单线程测试代码复现(等待解决!!!):
@Slf4j
public class ZooKeeperClientTest {
private static CountDownLatch connectedSignal = new CountDownLatch(1);
public static void main(String[] args) throws Exception{
ZKClientConfig config = new ZKClientConfig();
config.setProperty("zookeeper.sasl.client", "false");
config.setProperty("zookeeper.request.timeout", "6000");
ZooKeeper zookeeper = new ZooKeeper("122.51.112.187:2181", 6000, new Watcher() {
@Override
public void process(WatchedEvent event) {
Event.KeeperState state = event.getState();
// 连接成功
if (state == Event.KeeperState.SyncConnected) {
log.info("与ZooKeeper服务器连接成功");
connectedSignal.countDown();
}
// 断开连接
else if (state == Event.KeeperState.Disconnected){
log.info("与ZooKeeper服务器断开连接");
}
}} , true, config);
connectedSignal.await();
log.info("连接已建立...");
Stat stat = zookeeper.setData("/test2", "d3".getBytes(), -1);
log.info("已设置/test2数据");
//TimeUnit.SECONDS.sleep(2);
log.info("开始关闭连接");
zookeeper.close();
log.info("连接已关闭");
}
}
执行结果:
[2022-02-09 15:49:38] [ INFO ] [main-EventThread] [traceId:] com.wangan.test.ZooKeeperClientTest [codeline:32] - 与ZooKeeper服务器连接成功
[2022-02-09 15:49:38] [ INFO ] [main] [traceId:] com.wangan.test.ZooKeeperClientTest [codeline:43] - 连接已建立...
[2022-02-09 15:49:38] [ INFO ] [main] [traceId:] com.wangan.test.ZooKeeperClientTest [codeline:45] - 已设置/test2数据
[2022-02-09 15:49:38] [ INFO ] [main] [traceId:] com.wangan.test.ZooKeeperClientTest [codeline:48] - 开始关闭连接
[2022-02-09 15:49:38] [ WARN ] [main-SendThread(122.51.112.187:2181)] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:1294] - An exception was thrown while closing send thread for session 0x110e515ec2c0040.
org.apache.zookeeper.ClientCnxn$EndOfStreamException: Unable to read additional data from server sessionid 0x110e515ec2c0040, likely server has closed socket
at org.apache.zookeeper.ClientCnxnSocketNIO.doIO(ClientCnxnSocketNIO.java:77)
at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:350)
at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1290)
[2022-02-09 15:49:38] [ INFO ] [main-EventThread] [traceId:] org.apache.zookeeper.ClientCnxn [codeline:578] - EventThread shut down for session: 0x110e515ec2c0040
[2022-02-09 15:49:38] [ INFO ] [main] [traceId:] org.apache.zookeeper.ZooKeeper [codeline:1619] - Session: 0x110e515ec2c0040 closed
[2022-02-09 15:49:38] [ INFO ] [main] [traceId:] com.wangan.test.ZooKeeperClientTest [codeline:50] - 连接已关闭
ZooKeeper Is Hard
这里可以看到,使用ZooKeeper自带的客户端开发还是比较复杂的,因为它提供的都是建立连接、创建和修改path节点等比较基础的操作,对于分布式锁这样的综合场景需要做二次开发大量的代码,比如笔者上面的代码还不完善,所以一般来说都是使用其他ZooKeeper客户端,比如ZkClient和Curator等。