zookeeper 分散式鎖
阿新 • • 發佈:2018-12-09
推薦:
1. Curator 實現
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.0.1</version> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.13</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency>
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3) CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy); client.start(); InterProcessMutex lock = new InterProcessMutex(client, lockPath); if ( lock.acquire(maxWait, waitUnit) ) { try { // do some work inside of the critical section here } finally { lock.release(); } }
2. zookeeper 方式
下面就具體使用java和zookeeper實現分散式鎖,操作zookeeper使用的是apache提供的zookeeper的包。
- 通過實現Watch介面,實現process(WatchedEvent event)方法來實施監控,使CountDownLatch來完成監控,在等待鎖的時候使用CountDownLatch來計數,等到後進行countDown,停止等待,繼續執行。
- 以下整體流程基本與上述描述流程一致,只是在監聽的時候使用的是CountDownLatch來監聽前一個節點。
import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; 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; /** * Created by liuyang on 2017/4/20. */ public class DistributedLock implements Lock, Watcher { private ZooKeeper zk = null; // 根節點 private String ROOT_LOCK = "/locks"; // 競爭的資源 private String lockName; // 等待的前一個鎖 private String WAIT_LOCK; // 當前鎖 private String CURRENT_LOCK; // 計數器 private CountDownLatch countDownLatch; private int sessionTimeout = 30000; private List<Exception> exceptionList = new ArrayList<Exception>(); /** * 配置分散式鎖 * @param config 連線的url * @param lockName 競爭資源 */ public DistributedLock(String config, String lockName) { this.lockName = lockName; try { // 連線zookeeper zk = new ZooKeeper(config, sessionTimeout, this); Stat stat = zk.exists(ROOT_LOCK, false); if (stat == null) { // 如果根節點不存在,則建立根節點 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(); } } // 節點監視器 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 { // 等待鎖 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("鎖名有誤"); } // 建立臨時有序節點 CURRENT_LOCK = zk.create(ROOT_LOCK + "/" + lockName + splitStr, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); System.out.println(CURRENT_LOCK + " 已經建立"); // 取所有子節點 List<String> subNodes = zk.getChildren(ROOT_LOCK, false); // 取出所有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 (CURRENT_LOCK.equals(ROOT_LOCK + "/" + lockObjects.get(0))) { return true; } // 若不是最小節點,則找到自己的前一個節點 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; } // 等待鎖 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); // 計數等待,若等到前一個節點消失,則precess中進行countDown,停止等待,獲取鎖 this.countDownLatch.await(waitTime, TimeUnit.MILLISECONDS); this.countDownLatch = null; System.out.println(Thread.currentThread().getName() + " 等到了鎖"); } return true; } public void unlock() { try { System.out.println("釋放鎖 " + 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){ super(e); } public LockException(Exception e){ super(e); } } }
測試程式碼:
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("127.0.0.1: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();
}
}
}
我的實現方案:
import com.zte.daas.common.exception.GlobalErrException;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Slf4j
public class ExclusiveLock implements Watcher {
private CountDownLatch connectedSemaphore = new CountDownLatch(1);
private CountDownLatch lockCountDown = new CountDownLatch(1);
private Date currentDate = null;
private long timeOut = 0l;
private ZooKeeper zooKeeper = null;
private String lockResource = null;
private LockStatusEnum lockStatusEnum = LockStatusEnum.UN_LOCK;
public ExclusiveLock(String lockResource,String url) throws IOException, InterruptedException {
this.lockResource = lockResource;
zooKeeper = new ZooKeeper(url,5000,this);
connectedSemaphore.await(5, TimeUnit.SECONDS);
}
public boolean createLockNode(){
try {
zooKeeper.create(lockResource, "".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} catch (KeeperException e) {
return false;
} catch (InterruptedException e) {
return false;
}
return true;
}
public boolean lock(long timeOut) throws InterruptedException, KeeperException {
this.currentDate = new Date();
this.timeOut = timeOut;
tryLock();
lockCountDown.await(timeOut,TimeUnit.SECONDS);
if(LockStatusEnum.LOCKED == lockStatusEnum){
return true;
}
return false;
}
public void tryLock() throws InterruptedException, KeeperException {
//時間超時,返回
if(new Date().getTime()-currentDate.getTime()>timeOut*1000){
lockStatusEnum = LockStatusEnum.TRY_LOCK;
lockCountDown.countDown();
return;
}
if(createLockNode()){
lockStatusEnum = LockStatusEnum.LOCKED;
lockCountDown.countDown();
return;
}else{
Stat stat = zooKeeper.exists(lockResource,true);
if(stat == null){
tryLock();
}
}
return ;
}
public void unLock() throws KeeperException, InterruptedException {
deleteNode(lockResource);
}
public void deleteNode(String path) throws KeeperException, InterruptedException {
String pathFull = path;
if(path.equalsIgnoreCase("/zookeeper")){
return;
}
List<String> childNodeList = zooKeeper.getChildren(path,false);
if(childNodeList.size()>0){
for(String str :childNodeList){
if(pathFull.equals("/")){
deleteNode(pathFull+str);
}else {
deleteNode(pathFull+"/"+str);
}
}
}else{
zooKeeper.delete(path,-1);
}
}
public void close() throws InterruptedException {
if(zooKeeper != null){
zooKeeper.close();
}
}
//監聽
@Override
public void process(WatchedEvent watchedEvent) {
if(Event.KeeperState.SyncConnected == watchedEvent.getState()){
if(watchedEvent.getType() == Event.EventType.None){
connectedSemaphore.countDown();
return;
}else if(watchedEvent.getType() == Event.EventType.NodeDeleted){
log.info("watch----node delete");
try {
tryLock();
} catch (InterruptedException e) {
log.error("trylock:{}",e);
} catch (KeeperException e) {
log.error("trylock:{}",e);
}
}
}
}
}