zookeeper distributed lock

Posted by CoolAsCarlito on Sat, 01 Jun 2019 01:15:55 +0200


There are many distributed locks. redis can also implement distributed locks.

http://shangdc.blog.51cto.com/10093778/1914852 (see redis distributed locks)


zookeeper Distributed Lock Step:

1. Zookeeper is a node, similar to the file directory, so we abstract the lock into a directory. Zookeeper has a node of EPHEMERAL_SEQUENTIAL type. When multiple threads create a node by zookeeper, it will help us arrange the creation sequence, so the directory under this node is in order.

2. Get the smallest node of the current directory, and determine whether the smallest node is the current node of its own. If it means that the acquisition lock has been successful, if not the acquisition lock has failed.

3. When the acquisition of locks fails, in order to avoid the surprise effect, all you have to do is to get a node on the current node of your own, and then listen on that node. When the last node is deleted, it will trigger the listener and notify the node.

4. In this way, the next node will be notified when the lock is released.


What is the surprise effect: Understand that when a node deletes, all watcha subscribers to this node will regain the lock and compete. If the number is small or good, when the number is large, this design is unreasonable and wastes resources.


zookeeper's state and event type, check it out in advance.

Status KeeperState.Disconnected (0) Disconnected
 * KeeperState.SyncConnected (3) Synchronized connection status
 * KeeperState.AuthFailed (4) Authentication Failure State
 * KeeperState.ConnectedReadOnly (5) Read-only connection status
 * KeeperState.SaslAuthenticated(6) SASL Authenticated Pass Status
 * KeeperState.Expired (-112) expiration status
 * 
 *// EventType is the event type. Focus on Create Delete Data Changed ChildrenChanged
 * EventType.None (-1), none
 * EventType.NodeCreated (1),
 * EventType.NodeDeleted (2),
 * EventType.NodeDataChanged (3), [Node Data Changed (3)]
 * EventType.NodeChildrenChanged (4); Node subnode changes


Here is the code. Knock it down and understand it.

package com.lhcis.spider.system.annotation;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.CountDownLatch;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author sdc
 *
 */
public class ZooDistributeLock implements Watcher {

	private static final Logger LOG = LoggerFactory.getLogger(ZooDistributeLock.class);

	private static final String LOCK_PATH = "/zkLock";

	// Number of threads simulated open
	private static final int THREAD_NUM = 5;

	// Used to wait for all threads to connect successfully before performing tasks
	private static CountDownLatch startFlag = new CountDownLatch(1);

	// Used to ensure that all threads are executed
	private static CountDownLatch threadFlag = new CountDownLatch(THREAD_NUM);

	private ZooKeeper zk = null;

	private String currentPath;

	private String lockPath;

	public static void main(String[] args) {
		for (int i = 0; i < THREAD_NUM; i++) {
			final int j = i;
			new Thread() {
				@Override
				public void run() {
					ZooDistributeLock zooDistributeLock = new ZooDistributeLock();
					try {
						zooDistributeLock.connection();
						System.out.println("Connect" + j);
						zooDistributeLock.createNode();
						System.out.println("Establish" + j);
						zooDistributeLock.getLock();
						System.out.println("Get lock" + j);
					} catch (IOException | InterruptedException | KeeperException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
		try {
			threadFlag.await();
			LOG.info("All threads are executed...");
		} catch (InterruptedException e) {
			LOG.error(e.getMessage(), e);
		}
	}

	/**
	 * Disconnected For the events triggered when the network flashes off, of course, other things like pulling out the wire, killing zookeeper server, killing ZK
	 * connection It also triggers the event. SyncConnected re-select the next zk for the client side
	 * server Connect the triggered event, and the watcher is valid, that is, it can be perceived normally.
	 * Expired When the client reconnects to the server, the server finds that the session exceeds the set time and returns it to the client.
	 * Expired,At this point, watcher fails, that is, it cannot be perceived properly.
	 */
	@Override
	public void process(WatchedEvent event) {

		Event.KeeperState state = event.getState();
		Event.EventType type = event.getType();

		if (Event.KeeperState.SyncConnected == state) {
			if (Event.EventType.None == type) {
				// Identify successful connection
				LOG.info("Successful Connection ZK The server");
				startFlag.countDown();
			}

			if (Event.EventType.NodeDeleted == type && event.getPath().equals(this.lockPath)) {
				LOG.info("node:" + this.lockPath + "The lock has been released.");
				try {
					// The previous node is released, and the current node acquires the lock.
					getLock();
				} catch (KeeperException | InterruptedException e) {
					LOG.error(e.getMessage(), e);
				}
			}
		}

	}

	/**
	 * Connect to ZK
	 *
	 * @throws IOException
	 */
	private void connection() throws IOException, InterruptedException {

		zk = new ZooKeeper("127.0.0.1:2181", 5000, this);

		// Wait for the connection to succeed before proceeding to the next step
		startFlag.await();
	}

	// Create a node and initialize the current path
	private void createNode() throws KeeperException, InterruptedException, UnsupportedEncodingException {
		this.currentPath = this.zk.create(LOCK_PATH, "".getBytes("UTF-8"), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
	}

	private void getLock() throws KeeperException, InterruptedException {
		if (minNode()) {
			doSomething();
			// Release lock
			releaseLock();
		}
	}

	/**
	 * Is the current minimum node
	 *
	 * @return
	 */
	private boolean minNode() {

		// Current serial number
		try {
			initLockPath();
			// Judging that the previous node exists or does not exist, if it exists, it means that the current node is not the smallest node.
			// zk.getData(this.lockPath, this, new Stat());
			zk.getData(this.lockPath, true, new Stat());
			LOG.info(this.currentPath + " Not the minimum,No acquisition lock,wait for " + this.lockPath + " Release lock");
			return false;
		} catch (KeeperException e) {
			LOG.info(this.currentPath + " Minimum value,Get lock");
			return true;
		} catch (InterruptedException e) {
			LOG.error(e.getMessage(), e);
		}
		return true;
	}

	private void doSomething() {
		LOG.info("Processing business logic...");
	}

	/**
	 * Release the lock and close the connection
	 *
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	private void releaseLock() throws KeeperException, InterruptedException {
		Thread.sleep(2000);
		if (this.zk != null) {
			LOG.info(this.currentPath + " Business process completed,Release lock...");
			zk.delete(this.currentPath, -1);
			this.zk.close();
			LOG.info(Thread.currentThread().getName() + "Close zookeeper Connect");
		}
		threadFlag.countDown();
	}

	/**
	 * Initialize lockpath
	 */
	private void initLockPath() {

		int currentSeq = Integer.parseInt(this.currentPath.substring(LOCK_PATH.length()));

		// Last serial number
		int preSeq = currentSeq - 1;

		String preSeqStr = String.valueOf(preSeq);
		while (preSeqStr.length() < 10) {
			preSeqStr = "0" + preSeqStr;
		}
		this.lockPath = LOCK_PATH + preSeqStr;
	}

}



Reference code:

https://juejin.im/entry/596438bc6fb9a06bb47495f1


Topics: Java Zookeeper Apache Redis