zookeeper为什么能实现分布式锁
a. zookeeper的数据一致性(paxos)
b. zookeeper的临时顺序节点
c. zookeeper的监听watch机制
zookeeper实现原理
1. 所有尝试获取分布式锁的线程,会去zookeeper下一个持久化锁节点下面去创建临时的顺序节点。
2. 线程得到持久化节点的子节点(所有创建的临时顺序节点),排序,找到最小的节点,并判断是否是本线程创建的节点。
2.1 是 \> 获取到分布式锁
2.2 否 \> 2.2.1 .如果线程需要立即返回获取锁的结果。则返回失败
------ - - 2.2.2 如果线程一直等待直到获取锁。则监听本线程节点的排序前一个节点的节点删除事件(删除事件触发则回到第2步),线程阻塞。
3. 释放锁:删除本线程节点
流程图
这里假设锁根节点是lock ;
明白实现的原理及流程就差不多写出了,其他就是一些细节的问题了。
个人简单实现:
锁接口:
public interface DistributedLock {
/**
* 释放锁
*/
void unlock();
/**
* 尝试获得锁,能获得就立马获得锁,如果不能获得就立马返回
*/
boolean tryLock();
/**
* 尝试获得锁,一直阻塞,直到获得锁为止
*/
void lock();
}
锁实现类:
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
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 com.smart.mvc.util.StringUtils;
/**
*
* @author james
*/
public class ZookeeperDistributedLock implements DistributedLock {
/** session超时时间 */
private final int SESSION_TIMEOUT = 5000;
/** 持久化锁根节点 */
private String root = "/lock-";
/** 用于阻塞锁阻塞线程 */
private CountDownLatch countDownLatch = new CountDownLatch(1);
private ZooKeeper zookeeper;
/** 本线程创建的节点总路径(包括跟路径) */
private String lockPath;
/** 异常集合 */
private List<Exception> exception = new ArrayList<Exception>();
/**
* 连接zookeeper,并创建锁根节点
*
* @param 连接地址
*/
public ZookeeperDistributedLock(String address) {
if (StringUtils.isBlank(address)) {
throw new RuntimeException("Zookeeper address can not be empty !");
}
zookeeper = connectServer(address);
if (zookeeper != null) {
try {
Stat stat = zookeeper.exists(root, false);
if (stat == null) {
// 创建持久化节点
zookeeper.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
exception.add(e);
} catch (InterruptedException e) {
exception.add(e);
}
}
}
/**
* 连接zookeeper
*
* @param 连接地址
* @return
*/
private ZooKeeper connectServer(String address) {
// 使用CountDownLatch阻塞线程确保zookeeper实例成功
final CountDownLatch latch = new CountDownLatch(1);
ZooKeeper zk = null;
try {
zk = new ZooKeeper(address, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
latch.countDown();
}
}
});
latch.await();
} catch (IOException e) {
exception.add(e);
} catch (InterruptedException ex) {
exception.add(ex);
}
return zk;
}
/**
* 一直等待获取锁
*
* @throws KeeperException
* @throws InterruptedException
*/
public void lock() {
if (exception.size() > 0) {
throw new RuntimeException(exception.get(0));
}
try {
// 创建节点
creatNode(root + "/lock_");
// 尝试获取锁
judgeLock();
// 阻塞线程
countDownLatch.await();
} catch (Exception e) {
throw new RuntimeException(e.getCause());
}
}
/**
* 判断是否能够拥有该锁
*
* @throws Exception
*/
private void judgeLock() throws Exception {
try {
workWithSortList(root, list -> {
if (!lockPath.equals(root + "/" + list.get(0))) {
try {
waitForLock(list.get(Collections.binarySearch(list, lockPath.split("root/")[1]) - 1));
} catch (Exception e) {
throw new RuntimeException(e.getCause());
}
} else {
countDownLatch.countDown();
}
return null;
});
} catch (Exception e) {
throw new Exception(e.getCause());
}
}
/**
* 给前一个节点加上监听器
*
* @param nodePath
* @throws InterruptedException
* @throws KeeperException
*/
private void waitForLock(String nodePath) throws Exception {
zookeeper.exists(root + "/" + nodePath, new Watcher() {
// 节点删除时间
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
try {
judgeLock();
} catch (Exception e) {
throw new RuntimeException(e.getCause());
}
}
}
});
}
/**
* 释放锁,并删除节点
*/
public void unlock() {
try {
if (StringUtils.isNotBlank(lockPath)) {
zookeeper.delete(lockPath, -1);
}
} catch (InterruptedException e) {
} catch (KeeperException e) {
}
}
/**
* 尝试获得锁,能获得就立马获得锁并返回true,如果不能获得就立马返回false
*
* @return
*/
public boolean tryLock() {
try {
creatNode(root + "/lock_");
return workWithSortList(root, list -> {
if (lockPath.equals(root + "/" + list.get(0))) {
return true;
}
return false;
});
} catch (Exception e) {
throw new RuntimeException(e.getCause());
}
}
/**
* 获取到有序的子节点集合
*
* @param rootpath
* @param function
* @return
* @throws Exception
*/
public <R> R workWithSortList(String rootpath, Function<List<String>, R> function) throws Exception {
List<String> children = zookeeper.getChildren(rootpath, false);
Collections.sort(children);
if (children != null && !children.isEmpty()) {
return function.apply(children);
}
return null;
}
/**
* 创建临时有序节点
*
* @param path
* @return
* @throws Exception
*/
public String creatNode(String path) throws Exception {
return lockPath = zookeeper.create(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
}