At first, I saw the design idea of Zookeeper to realize distributed lock, which is similar to Redis written before link , a node is created through only one client. As long as there are other nodes in the node that cannot be created, a resource can be occupied. Later, I think that the lock is not just Zookeeper and Redis. Even the lock between threads in a single process is actually an object resource control. After much contact, I found that the distributed locks of Zookeeper and Redis are still different~
1, Let's take a look at some parts similar to Redis's Zookeeper distributed locks

As shown in the figure, if our three clients listen to the same node, only one client, such as client A, can be created successfully. Because the other nodes listen to node events, when A changes the node state after use, the other nodes know that they can start to seize resources again.
Herding: however, this has a disadvantage. Since many nodes listen to the same event, although only one node can seize resources every time a node change occurs, it startles the whole group and causes many event changes. Obviously, this is not cost-effective for distributed applications of many nodes.
2, Using ordered nodes to realize distributed locking

As shown in the figure, each client creates its own ordered temporary node under the lock node. The temporary node will listen to a node smaller than itself. If it is the smallest at present, it means that you can be used normally (preempting resources). Here, the extended implementation is based on the Lock provided by Java, and the watcher mechanism is added in an inherited way. The ZK operation uses the native Java API provided by ZK.
Distributed lock code
public class DistributedLock implements Lock, Watcher { private ZooKeeper zk=null;//Create zk client private String ROOT_LOCK="/locks";//Define a root node private String WAIT_LOCK;//Wait for the previous lock private String CURRENT_LOCK;//Indicates the current lock private CountDownLatch countDownLatch; public DistributedLock( ) { try { this.zk = new ZooKeeper("Ip:port", 4000, this);//Here, the watcher is implemented in this class //Determine whether the root node exists Stat stat=zk.exists(ROOT_LOCK,false); if (stat==null){ zk.create(ROOT_LOCK,"0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } } @Override public boolean tryLock() {//When a node calls a method that attempts to obtain a lock try { //Create a temporary ordered node and copy it to the current lock CURRENT_LOCK=zk.create(ROOT_LOCK+"/","0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);//Create temporary ordered node System.out.println(Thread.currentThread().getName()+"Create temporary node--->"+CURRENT_LOCK+"--->Attempt to acquire lock"); //Get child node (lock node) List<String> childrens = zk.getChildren(ROOT_LOCK, false); //Create a child Set and add it to the ordered Set SortedSet<String> nodes=new TreeSet<>(); for (String children:childrens){ nodes.add(ROOT_LOCK+"/"+children); } String firstNode = nodes.first(); //Try to obtain a smaller result set than the current node, so as to obtain the last largest node, that is, the previous node of this node //Note that nodes cannot be used here Last(), because the last node obtained in this way is the last node of all nodes, which is greater than this node SortedSet<String> lessThenMe = ((TreeSet<String>)nodes).headSet(CURRENT_LOCK); if (CURRENT_LOCK.equals(firstNode)){//If the current node is the smallest node in the set, it obtains a lock return true; } if (!lessThenMe.isEmpty()){//If there is a node smaller than the current node WAIT_LOCK=lessThenMe.last();//Obtain the last node of the ordered combination, that is, the previous node, and set it to WAIT_LOCL System.out.println("lessThenMe the last one : "+lessThenMe.last()); } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return false; } @Override public void lock() { if (this.tryLock()){//If the lock is obtained successfully System.out.println(Thread.currentThread().getName()+"Lock obtained successfully"); return; } //If the lock is not successfully obtained, wait for the previous node to release the lock waitForLock(WAIT_LOCK); } private boolean waitForLock(String preNode){ try { //Listen to the last node smaller than yourself Stat stat=zk.exists(preNode,true); if (stat!=null){ System.out.println(Thread.currentThread().getName()+"wait for--->"+ROOT_LOCK+"/"+preNode+"Release lock"); countDownLatch=new CountDownLatch(1); countDownLatch.await(); System.out.println(Thread.currentThread().getName()+"--->Acquire lock"); } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return true; } @Override public void lockInterruptibly() throws InterruptedException { } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } @Override public void unlock() { System.out.println(Thread.currentThread().getName()+"Release lock"+CURRENT_LOCK); try { zk.delete(CURRENT_LOCK,-1);//version=-1 will be deleted anyway CURRENT_LOCK=null; zk.close(); } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } } @Override public Condition newCondition() { return null; } @Override public void process(WatchedEvent watchedEvent) { if (this.countDownLatch!=null){ this.countDownLatch.countDown(); } } }
Test class
public class ZkDistributedLockTest { public static void main(String[] args) throws IOException { CountDownLatch countDownLatch=new CountDownLatch(10); for(int i=0;i<10;i++){ new Thread(()->{ try { countDownLatch.await(); DistributedLock distributedLock=new DistributedLock(); distributedLock.lock();//The current thread attempted to acquire a lock } catch (InterruptedException e) { e.printStackTrace(); } },"thread "+i).start(); countDownLatch.countDown(); } System.in.read(); } }
Say something about your thoughts:
1. A countdown countdown latch [1] is set here to control and block concurrent threads; 2. We let each thread create a distributed lock tool class * * DistributedLock. Each thread will connect to the ZK server and become a client. There is only one creation node
3. Next, each thread tries to call our lock() method, and our lock will call the tryLock() method 4. First, let each thread create a temporary order node in tryLock(), which is independent of the thread name, and the first to arrive is sorted first
5. Then, we will find out all the sequential nodes under the distributed lock root node at this time. If the current node is the smallest node, it will obtain the lock. If the current node is not the smallest node, find the previous node and listen, set the countdown to 1 by using countdowncatch, and block the current thread [2] 6. When a listening event (deletion or change) occurs at a node, we release countdowncatch and release this thread
In this way, we can realize the sequential action of Zk nodes in the form of distributed locks.
The above provides a self implemented watcher and lock, which simply implements distributed locks using the native zk API. In fact, curator provides many highly encapsulated distributed locks, simplifies the steps, and provides more detailed operations, such as the election of disconnection in the read-write supermarket~

eg
reference: [1] Countdownlaunch parsing https://www.jianshu.com/p/a1a73ce99526 [2] Using SortedSet to sort nodes, SortedSet Headset (string key), which can take out nodes smaller than the key, SortedSet First get the first node, SortedSet Last gets the specific of the last node https://blog.csdn.net/xjk201/article/details/81586209
Ha ha ha, writing a paper is in the dark.