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等。

posted on 2022-02-09 15:33  肥兔子爱豆畜子  阅读(102)  评论(0编辑  收藏  举报

导航