Realize distributed lock based on ZK

introduction

ZooKeeper is a distributed, open source distributed application coordination service, an open source implementation of Google’s Chubby, and an important component of Hadoop and Hbase. It is a software that provides consistent services for distributed applications. The functions provided include: configuration maintenance, domain name services, distributed synchronization, group services, etc.

The architecture of ZooKeeper achieves high availability through redundant services. Therefore, if there is no response the first time, the client can ask another ZooKeeper host. ZooKeeper nodes store their data in a hierarchical namespace, very similar to a file system or a prefix tree structure. The client can read and write on the node, thus having a shared configuration service in this way. Updates are in total order.

Based on the principle of ZooKeeper distributed lock

  1. In zookeeper create a temporary order of nodes node_n specified node (locks)

  2. Get all children under locks

  3. Sort the child nodes according to the node's self-increasing sequence number from small to large

  4. Determine if the node is the first child node, if it is, then acquire the lock; if not, listen for the deletion event of the node smaller than the node

  5. If the monitoring event takes effect, return to the second step to re-judgment until the lock

Implementation

The following specifically uses java and zookeeper to implement distributed locks, and the zookeeper operation uses the zookeeper package provided by apache.

  • By implementing the Watch interface and implementing the process (WatchedEvent event) method to implement monitoring, make CountDownLatch complete the monitoring, use CountDownLatch to count when waiting for the lock, wait until the countDown is performed, stop waiting, and continue to run.

  • The following overall process is basically the same as the process described above, except that CountDownLatch is used to monitor the previous node when monitoring.

package org.java.base.zookeeper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.codehaus.jettison.json.JSONString;

import com.fasterxml.jackson.databind.util.JSONPObject;
import com.google.gson.JsonObject;


public class DistributedLock implements Lock, Watcher {
 private ZooKeeper zk = null;
 // root node
 private String ROOT_LOCK = "/locks";
 // competing resources
 private String lockName;
 // the previous lock waiting
 private String WAIT_LOCK;
 // current lock
 private String CURRENT_LOCK;
 // counter
 private CountDownLatch countDownLatch;
 private int sessionTimeout = 30000;
 private List<Exception> exceptionList = new ArrayList<Exception>();

 /**
 * Configure distributed lock
 * @param config connected url
 * @param lockName competing resources
 */
 public DistributedLock(String config, String lockName) {
 this.lockName = lockName;
 try {
 // Connect to zookeeper
 zk = new ZooKeeper(config, sessionTimeout, this);
 Stat stat = zk.exists(ROOT_LOCK, false);
 if (stat == null) {
 // If the root node does not exist, create the root node
 zk.create(ROOT_LOCK, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
 }
 } catch (IOException e) {
 e.printStackTrace ();
 } catch (InterruptedException e) {
 e.printStackTrace ();
 } catch (KeeperException e) {
 e.printStackTrace ();
 }
 }

 // node monitor
 public void process(WatchedEvent event) {
 if (this.countDownLatch != null) {
 this.countDownLatch.countDown();
 }
 }

 public void lock() {
 if (exceptionList.size() > 0) {
 throw new LockException(exceptionList.get(0));
 }
 try {
 if (this.tryLock()) {
 System.out.println(Thread.currentThread().getName() + " " + lockName + "获得了锁");
 return;
 } else {
 // wait for the lock
 waitForLock(WAIT_LOCK, sessionTimeout);
 }
 } catch (InterruptedException e) {
 e.printStackTrace ();
 } catch (KeeperException e) {
 e.printStackTrace ();
 }
 }

 public boolean tryLock() {
 try {
 String splitStr = "_lock_";
 if (lockName.contains(splitStr)) {
 throw new LockException("The lock name is wrong");
 }
 // Create a temporary ordered node
 CURRENT_LOCK = zk.create(ROOT_LOCK + "/" + lockName + splitStr, new byte[0],
 ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
 System.out.println(CURRENT_LOCK + "Created");
 // Take all child nodes
 List<String> subNodes = zk.getChildren(ROOT_LOCK, false);
 // Take out all locks of lockName
 List<String> lockObjects = new ArrayList<String>();
 for (String node : subNodes) {
 String _node = node.split(splitStr)[0];
 if (_node.equals(lockName)) {
 lockObjects.add(node);
 }
 }
 Collections.sort(lockObjects);
 System.out.println(Thread.currentThread().getName() + " 的锁是 " + CURRENT_LOCK);
 // If the current node is the smallest node, the lock is successfully acquired
 if (CURRENT_LOCK.equals(ROOT_LOCK + "/" + lockObjects.get(0))) {
 return true;
 }

 // If it is not the smallest node, find its previous node
 String prevNode = CURRENT_LOCK.substring(CURRENT_LOCK.lastIndexOf("/") + 1);
 WAIT_LOCK = lockObjects.get(Collections.binarySearch(lockObjects, prevNode) - 1);
 } catch (InterruptedException e) {
 e.printStackTrace ();
 } catch (KeeperException e) {
 e.printStackTrace ();
 }
 return false;
 }

 public boolean tryLock(long timeout, TimeUnit unit) {
 try {
 if (this.tryLock()) {
 return true;
 }
 return waitForLock(WAIT_LOCK, timeout);
 } catch (Exception e) {
 e.printStackTrace ();
 }
 return false;
 }

 // wait for the lock
 private boolean waitForLock(String prev, long waitTime) throws KeeperException, InterruptedException {
 Stat stat = zk.exists(ROOT_LOCK + "/" + prev, true);

 if (stat != null) {
 System.out.println(Thread.currentThread().getName() + "等待锁 " + ROOT_LOCK + "/" + prev);
 this.countDownLatch = new CountDownLatch(1);
 // Count and wait, if you wait until the previous node disappears, then countDown in precess, stop waiting, and acquire the lock
 this.countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);
 this.countDownLatch = null;
 System.out.println(Thread.currentThread().getName() + "Wait until the lock is reached");
 }
 return true;
 }

 public void unlock() {
 try {
 System.out.println("Release the lock" + CURRENT_LOCK);
 zk.delete(CURRENT_LOCK, -1);
 CURRENT_LOCK = null;
 zk.close();
 } catch (InterruptedException e) {
 e.printStackTrace ();
 } catch (KeeperException e) {
 e.printStackTrace ();
 }
 }

 public Condition newCondition() {
 return null;
 }

 public void lockInterruptibly() throws InterruptedException {
 this.lock();
 }


 public class LockException extends RuntimeException {
 private static final long serialVersionUID = 1L;
 public LockException(String e){
 overcome);
 }
 public LockException(Exception e){
 overcome);
 }
 }
}

Test class

package org.java.base.zookeeper;
/**
 * There is a problem with this code, see if you can find it?
 * @author Liuhaihua
 *
 */
public class Test {
 static int n = 500;
 
 public static void secskill() {
 System.out.println(--n);
 }

 public static void main(String[] args) {
 
 Runnable runnable = new Runnable() {
 public void run() {
 DistributedLock lock = null;
 try {
 lock = new DistributedLock("10.152.17.73:2181", "test1");
 lock.lock();
 secskill ();
 System.out.println(Thread.currentThread().getName() + "正在运行");
 } finally {
 if (lock != null) {
 lock.unlock();
 }
 }
 }
 };

 for (int i = 0; i < 10; i++) {
 Thread t = new Thread(runnable);
 t.start();
 }
 }
}


Guess you like

Origin blog.51cto.com/15082402/2644329