最近做一个RPC的框架,注册中心用的zk,为了给服务端每个提供服务的method都给编号,用到了分布式锁,正好就用zk实现了。
zookeeper实现的分布式锁我所知的有两种:
1.一种是利用zookeeper在创建子节点时候会保证对于一个znode只有一个客户端会创建成功的特性来实现的排他锁。
2.另一种就是利用zookeeper创建一系列的顺序节点,将这些节点排好序后,当前第一个节点获取锁,其它的节点(监测节点)依次的对它前一个节点(前节点)添加watcher,前节点删除时候,监测节点收到通知,此时监测节点获取到锁,进行操作后删除本节点,释放锁,这种锁称为共享锁。
以下就是一个共享锁的简单实现
package com.qiyi.rpc.zookeeper;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
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;
import com.alibaba.fastjson.JSONObject;
public class DistributedLock implements Watcher {
private CountDownLatch countDown;
private static String lockRoot = "/qiyiLock";
/** 上一个等待锁 **/
private String beforeLock;
private static ZooKeeper zk;
/** 本节点 **/
private String selfLock;
private static Logger logger = LoggerFactory
.getLogger(DistributedLock.class);
@Override
public void process(WatchedEvent event) {
logger.info("节点:{}收到通知", selfLock);
if (this.countDown != null) {
this.countDown.countDown();
}
}
public void lock() {
if (tryLock()) {
unlock();
} else {
if (wait(beforeLock, null)) {
unlock();
}
}
}
public void unlock() {
try {
Thread.sleep(1000);
logger.info("thread:{},释放锁,删除节点:{}",
Thread.currentThread().getId(), selfLock);
zk.delete(lockRoot + "/" + selfLock, -1);
selfLock = null;
// zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
public boolean tryLock() {
String lockName = "qiyiLocks-";
try {
/** 创建本节点 **/
selfLock = zk.create(lockRoot + "/" + lockName, new byte[0],
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
selfLock = selfLock.substring(selfLock.lastIndexOf("/") + 1);
logger.info("thread:{},创建锁节点:{}", Thread.currentThread().getId(),
selfLock);
/** 获取根节点下子节点列表 **/
List<String> childNodes = zk.getChildren(lockRoot, false);
/** 将锁下面的子节点列表排序 **/
Collections.sort(childNodes);
logger.info("thread:{},获取到子节点列表:{}",
Thread.currentThread().getId(),
JSONObject.toJSONString(childNodes));
int selfIndex = childNodes.indexOf(selfLock);
if (selfIndex == 0) {
logger.info("thread:{},当前节点是锁节点:{}", Thread.currentThread()
.getId(), selfLock);
return true;
} else if (selfIndex < 0) {
return false;
}
beforeLock = childNodes.get(selfIndex - 1);
logger.info("获取到上一个节点:{},本节点:{}", beforeLock, selfLock);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public boolean wait(String beforeLock, Long timeout) {
/** 添加watcher **/
try {
logger.info("监控节点:{},本节点:{}", lockRoot + "/" + beforeLock, selfLock);
Stat stat = zk.exists(lockRoot + "/" + beforeLock, this);
if (stat != null) {
this.countDown = new CountDownLatch(1);
if (timeout != null) {
this.countDown.await(timeout, TimeUnit.MILLISECONDS);
} else {
this.countDown.await();
}
}
return true;
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public static void initConnect() {
try {
zk = new ZooKeeper("192.168.1.111:2181", 5000, null);
/** 创建锁节点 **/
logger.info("创建根节点:{}", lockRoot);
zk.create(lockRoot, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
} catch (IOException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService e = Executors.newFixedThreadPool(100);
DistributedLock.initConnect();
for (int i = 0; i < 100; i++) {
Runnable r = new Runnable() {
@Override
public void run() {
DistributedLock d = new DistributedLock();
d.lock();
}
};
e.execute(r);
}
Thread.sleep(Long.MAX_VALUE);
}
}
下面是部分结果: