概念
zk分布式锁,简单一点说就是某个节点尝试创建临时znode,此时创建成功了就获取了这个锁;这个时候别的客户端来创建锁会失败,只能注册个监听器监听这个锁。释放锁就是删除这个znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新枷锁。
redis和zk分布式比较
redis分布式锁和zk分布式锁的对比:
redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能;
zk分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
另外一点就是,如果是redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁;而zk的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁。
zk锁直接上代码
import org.apache.zookeeper.*;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
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 static java.lang.Thread.sleep;
/**
* 分布式锁的机制
*/
public class DistributedLock implements Lock, Watcher {
private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLock.class);
private static final int SESSION_TIMEOUT = 2000*100;
private String lockName;
private String root = "/locks";
private String pathSeperator = "/";
private String idSeperator = "-";
private String lockPath;
private String waitNode;
private String myNode;
private CountDownLatch latch;
private String host;
protected ZooKeeper zk;
private CountDownLatch connectedSignal = new CountDownLatch(1);
public DistributedLock(String host, String lockName) throws IOException, InterruptedException, KeeperException {
this.host = host;
zk = new ZooKeeper(host, SESSION_TIMEOUT, this);
this.lockName = lockName;
lockPath = root + pathSeperator + lockName;
connectedSignal.await();
init();
}
private void init() throws InterruptedException, KeeperException {
try {
Stat stat = zk.exists(root, false);
if (stat == null) {
// 创建根节点
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (KeeperException ke) {
if (KeeperException.Code.NODEEXISTS == ke.code()) {
LOGGER.info("锁根目录已经创建 {}", root);
} else {
throw ke;
}
}
try {
Stat stat = zk.exists(lockPath, false);
if (stat == null) {
// 创建根节点
zk.create(lockPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
LOGGER.info("创建锁节点 {} 成功", lockPath);
} else {
LOGGER.info("无需创建锁目录{}", lockPath);
}
} catch (KeeperException ke) {
if (KeeperException.Code.NODEEXISTS == ke.code()) {
LOGGER.info("无需创建锁目录 {}", lockPath);
} else {
throw ke;
}
}
}
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
connectedSignal.countDown();
return;
}
if (event.getState() == Event.KeeperState.Expired) {
try {
LOGGER.info("zk session 超时,开始重连");
zk = new ZooKeeper(host, SESSION_TIMEOUT, this);
} catch (IOException e) {
LOGGER.error("zk session 超时,重连失败",e);
}
return;
}
if (event.getType() == Event.EventType.NodeDeleted && this.latch != null) {
this.latch.countDown();
}
}
@Override
public void lock() {
if (tryLock()) {
return;
} else {
waitForLock(-1L, null);
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
String requiredPath = lockPath + pathSeperator +"lock" + idSeperator;
try {
myNode = zk.create(requiredPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
LOGGER.info("创建节点成功{}",myNode);
} catch (KeeperException | InterruptedException e) {
LOGGER.error("创建节点异常{}", myNode,e);
return false;
}
return judgeLock();
}
@Override
public void unlock() {
deleteNode();
if (zk != null) {
try {
zk.close();
} catch (InterruptedException e) {
LOGGER.error("关闭连接异常",e);
}
}
}
@Override
public Condition newCondition() {
return null;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
if (this.tryLock()) {
return true;
} else {
return waitForLock(time, unit);
}
}
private boolean doJudgeLock() throws KeeperException, InterruptedException {
List<String> lockNodes = zk.getChildren(lockPath, false);
if (lockNodes != null && lockNodes.size() > 0) {
Collections.sort(lockNodes);
String nodeName = myNode.substring(myNode.lastIndexOf("/")+1);
LOGGER.info("判断节点位置,得到当前节点{}", myNode);
if (lockNodes.indexOf(nodeName) == -1) {
LOGGER.info("创建后但找不到本节点,网络问题{}", myNode);
return false;
} else if (lockNodes.indexOf(nodeName) == 0) {
LOGGER.info("得到锁{}", myNode);
return true;
} else {
waitNode = lockPath+pathSeperator+lockNodes.get(lockNodes.indexOf(nodeName)-1);
LOGGER.info("得到前置节点{}", waitNode);
return false;
}
} else {
LOGGER.info("创建成功过后却得到空列表");
return false;
}
}
private boolean judgeLock() {
try {
return doJudgeLock();
} catch (KeeperException | InterruptedException e) {
LOGGER.error("获取分布式锁错误", e);
return false;
}
}
private boolean waitForLock(Long time, TimeUnit unit) {
Stat stat;
try {
LOGGER.info("查看锁状态{}",waitNode);
final Watcher previousListener = new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
LOGGER.info("监听到前置节点释放信息{}",waitNode);
if(latch != null) {
latch.countDown();
latch = null;
} else {
LOGGER.info("未触发latch");
}
}
}
};
stat = zk.exists(waitNode, previousListener);
} catch (KeeperException | InterruptedException e) {
LOGGER.error("等待锁zk错误", e);
return false;
}
if (stat != null) {
try {
LOGGER.info("监听锁,等待前置节点{} 事件,", waitNode);
latch = new CountDownLatch(1);
long startTime = System.currentTimeMillis();
if (time > 0) {
latch.await(time, unit);
long endTime = System.currentTimeMillis();
if ((endTime - startTime) > unit.toMillis(time)) {
LOGGER.info("等待锁超时 {}", endTime - startTime);
deleteNode();
return false;
} else {
return judgeLock();
}
} else {
latch.await();
return judgeLock();
}
} catch (InterruptedException e) {
LOGGER.error("等待锁过程中异常", e);
return false;
}
} else {
LOGGER.info("获取前置节点存在信息为null", waitNode);
return true;
}
}
private void deleteNode() {
try {
Stat stat = zk.exists(myNode,false) ;
if(stat != null) {
zk.delete(myNode, -1);
LOGGER.info("删除节点成功{}",myNode);
}
} catch (InterruptedException | KeeperException e) {
LOGGER.error("删除节点异常", e);
}
}
public static void main(String[] args) {
Thread a = new Thread(
new Runnable() {
@Override
public void run() {
DistributedLock lock = null;
try {
//我的zookeeper地址:192.168.40.204:2181
lock = new DistributedLock("192.168.40.204:2181", "testlock");
lock.lock();
LOGGER.info("a得到锁了,xiumian ");
sleep(5000);
} catch (IOException e) {
LOGGER.error("a获锁失败",e);
} catch (InterruptedException e) {
LOGGER.error("a获锁失败",e);
} catch (KeeperException e) {
LOGGER.error("a获锁失败",e);
} finally {
lock.unlock();
LOGGER.info("a释放锁了,xiumian ");
}
}
}
);
Thread b = new Thread(
new Runnable() {
@Override
public void run() {
DistributedLock lock = null;
try {
lock = new DistributedLock("192.168.40.204:2181", "testlock");
lock.lock();
sleep(1500);
LOGGER.info("b得到锁了");
} catch (IOException e) {
LOGGER.error("b获锁失败", e);
} catch (InterruptedException e) {
LOGGER.error("b获锁失败", e);
} catch (KeeperException e) {
LOGGER.error("b获锁失败", e);
} finally {
lock.unlock();
LOGGER.info("b释放锁了 ");
}
}
}
);
Thread c = new Thread(
new Runnable() {
@Override
public void run() {
DistributedLock lock = null;
try {
lock = new DistributedLock("192.168.40.204:2181", "testlock");
lock.lock();
sleep(2000);
LOGGER.info("c得到锁了");
} catch (IOException e) {
LOGGER.error("c获锁失败", e);
} catch (InterruptedException e) {
LOGGER.error("c获锁失败", e);
} catch (KeeperException e) {
LOGGER.error("c获锁失败", e);
} finally {
lock.unlock();
LOGGER.info("c释放锁了 ");
}
}
}
);
Thread d = new Thread(
new Runnable() {
@Override
public void run() {
DistributedLock lock = null;
try {
lock = new DistributedLock("192.168.40.204:2181", "testlock");
lock.lock();
sleep(2000);
LOGGER.info("d得到锁了");
} catch (IOException e) {
LOGGER.error("d获锁失败", e);
} catch (InterruptedException e) {
LOGGER.error("d获锁失败", e);
} catch (KeeperException e) {
LOGGER.error("d获锁失败", e);
} finally {
lock.unlock();
LOGGER.info("d释放锁了 ");
}
}
}
);
Thread e = new Thread(
new Runnable() {
@Override
public void run() {
DistributedLock lock = null;
try {
lock = new DistributedLock("192.168.40.204:2181", "testlock");
lock.lock();
sleep(2000);
LOGGER.info("e得到锁了");
} catch (IOException e) {
LOGGER.error("e获锁失败", e);
} catch (InterruptedException e) {
LOGGER.error("e获锁失败", e);
} catch (KeeperException e) {
LOGGER.error("e获锁失败", e);
} finally {
lock.unlock();
LOGGER.info("e释放锁了 ");
}
}
}
);
a.start();
b.start();
c.start();
d.start();
d.start();
}
}