基于ZOOKEEPER的一个实现了Lock接口的分布式锁
来源:互联网 发布:软件评测师视频 编辑:程序博客网 时间:2024/06/01 10:17
网上有很多分布式锁的实现都是基于zookeeper的,看得多了,也就想自己试着写一下,下面和大家分享下,本人水平很菜,有什么不好的地方大家给我建议吧~ 先说下大概的思路: **整个实现原理非常简单,就是每一个单独的进程都有自己唯一的标识,这个标识用UUID去生成,从而保证在同一台机器获取多台机器的不同进程间都是唯一的。 然后内部维护了一个ReentrantLock 本地锁对象,每个操作都必须先得到本地锁才能尝试得到分布式锁,可以避免下动不动就请求zookeeper的情况。 之后,构造方法中的lockId是唯一标识一个锁的,只要lockId相同,都会去竞争同一个锁,竞争的方法是看谁先在zookeeper上能够成功地创建出的目录(这个目录只和lockId有关,所以lockId相同的多个锁对象只能有一个持有锁,至于创建时怎样去保证唯一性这个复杂的问题就交给zookeeper去做了),创建目录成功的对象持有锁,释放锁的时候就是删除目录,删除目录后会触发预先定义好的watcher监听代码(这个逻辑也是zk去做的),让原本在等待中的各个对象重新去竞争这个锁,同样地,最终的胜利者只有一个,而万一原本的持锁对象还没释放就因一些乱七八糟的情况挂掉了,在这种情况下,因为创建的是临时目录,zk对临时目录的处理方式是创建这个目录的对象和zookeeper的连接丢失后这个目录就会收回去,也就等同于删除了这个目录了,原本处于等待中的其它对象也可以投入到新的竞争中了(zk太方便了,用它来实现分布式锁简直就是傻瓜式操作)...总体而言zookeeprt去实现分布式锁的大概思路就是这样。** 下面是一些具体的代码实现: 首先是一些基本接口的定义(操作zookeeper的接口):
public interface ISDClient { boolean createPath(String path,String data);//创建指定路径的临时目录 boolean deletePath(String path);//删除指定路径的目录 boolean isExists(String path,Watcher watcher);//判断某目录是否存在并进行监听 void createPersistentPath(String path);//创建永久性目录 }
这个接口主要是封装了一些基本的和分布式锁的实现有关的操作,具体的实现也不仅仅限于ZK,只要实现了这个接口的都能够用,不过需要能够区分临时目录和永久目录,永久目录是建完后就一直在的,临时目录是连接丢失后就收回的。 下面是这个接口的具体实现;
public class SZKClient implements ISDClient { private ZooKeeper zooKeeper = null; public SZKClient(String host,int port){ try { zooKeeper = new ZooKeeper(host, port ,null); } catch (IOException e) { throw new RuntimeException(e); } } public void createPersistentPath(String path){ String [] paths = path.split("/"); String thisPath = ""; for(String tempPath : paths){ if(tempPath == null || tempPath.length() == 0) continue; thisPath += "/"+tempPath; try { this.zooKeeper.create(thisPath, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (Exception e) {} } } public boolean createPath(String path,String data) { try { this.zooKeeper.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); return true; } catch (Exception e) { return false; } } public boolean deletePath(String path) { try { this.zooKeeper.delete(path, -1); return true; } catch (Exception e) { return false; } } public boolean isExists(String path, Watcher watcher) { Stat state = null; try { state = this.zooKeeper.exists(path, watcher); } catch (Exception e) { e.printStackTrace(); } return state!=null; }}
再然后是创建的watcher监听类,这个监听类用于监听指定目录的变更(主要是目录删除后会触发process方法,从而让等待中的锁对象能够获取到锁),因为zookeeper只会激发一次注册过的监听类,所以当获取锁失败后,需要重新注册一次,参见fail方法:
public abstract class LockWatcher implements Watcher { private ISDClient zkClient = null; private String path = null; private String lockContent = null; public LockWatcher(ISDClient zkClient,String path,String lockContent){ this.zkClient = zkClient; this.path = path; this.lockContent = lockContent; } public void process(WatchedEvent wevent) { //只要事件触发就进行创建目录操作 if(this.zkClient.createPath(path, lockContent)){ try{ this.success(lockContent); }catch(Exception e){ //如果回调方法出现异常,释放锁 this.zkClient.deletePath(path); this.fail(); throw new RuntimeException("出现异常"); } }else{ this.fail(); } } public abstract void success(String result); public void fail(){ this.zkClient.isExists(this.path, this); };}
所有准备类都准备完成后,最后就是最最核心的实现了——SDLOCK,当然咯,SDLOCK这个名字是随便起的...:
public class SDLock implements Lock{ private String lockId; private static String distributionId; //唯一标识当前进程的ID private Lock localLock = null; //本地锁,可适当避免频繁进行网络通讯,在一个进程需要进行锁的时候可在本地进行处理,只有获得了本地锁才能尝试去获得分布式锁。 private static final String ZKPATH = "/sblock/lock/"; public static String APP_NAME = "_SDLOCK_"; //和使用同一个zk的其它应用作区分 private String lockPath = null; private ISDClient zkClient = null; private String _reallyLockId; //真正唯一标识当前锁对象的 //产生唯一标识此进程的UUID static{ SDLock.distributionId = UUID.randomUUID().toString(); } private String curLockId = null; private synchronized void setCurLockId(String lockId){ curLockId = lockId; } /** * SDLock的构造函数 * SDLock需要接受一个唯一标识,这个标识是在多台机器上唯一区分一个分布式锁的,同一个ID意味着在多个分布式环境下是同一个锁 * @param lockId */ public SDLock(String lockId,ISDClient zkClient){ this.lockId = lockId; this.localLock = new ReentrantLock(); this.zkClient = zkClient; curLockId = ""; this._reallyLockId = distributionId + "_" + Thread.currentThread().getId() + "_" + this.lockId; this.zkClient.createPersistentPath(ZKPATH+APP_NAME); } public void lock() { //先获取本地锁 this.localLock.lock(); //再尝试获取分布式锁 if(this.zkClient.createPath(this.getLockPath(), this._reallyLockId)){ curLockId = this._reallyLockId; return; //成功获取锁,返回 }else{ this.zkClient.isExists(this.getLockPath(), new LockWatcher(zkClient,this.getLockPath(),this._reallyLockId){ @Override public void success(String result) { setCurLockId(result); } }); //阻塞等待 try { for(;;){ TimeUnit.MILLISECONDS.sleep(500); //如果当前的锁对象和已经获得锁的锁对象一致,释放 if(this._reallyLockId.equals(curLockId)) return; } } catch (InterruptedException e) { throw new RuntimeException(e); } } } public void lockInterruptibly() throws InterruptedException { //判断线程是否中断,如果线程发生中断,则抛出异常 if(Thread.currentThread().isInterrupted()) throw new InterruptedException(); this.lock(); } public boolean tryLock() { boolean getLocalLock = this.localLock.tryLock(); if(!getLocalLock) return false; try{ if(getLocalLock){ //再尝试获取分布式锁 if(this.zkClient.createPath(this.getLockPath(), this._reallyLockId)){ curLockId = this._reallyLockId; return true; //成功获取锁,返回 }else{ return false; } }else return false; }catch(Exception e){ if(getLocalLock) this.localLock.unlock(); return false; }finally{ this.localLock.unlock(); } } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { //先获取本地锁 boolean getLocalLock = this.localLock.tryLock(time,unit); if(!getLocalLock) return false; try{ //再尝试获取分布式锁 if(this.zkClient.createPath(this.getLockPath(), this._reallyLockId)){ curLockId = this._reallyLockId; return true; //成功获取锁,返回 }else{ this.zkClient.isExists(this.getLockPath(), new LockWatcher(zkClient,this.getLockPath(),this._reallyLockId){ @Override public void success(String result) { setCurLockId(result); } }); long waitTime = 0; long totalWaitTime = unit.toMillis(time); //阻塞等待 try { for(;;){ unit.sleep(500); //如果当前的锁对象和已经获得锁的锁对象一致,释放 if(this._reallyLockId.equals(curLockId)) return true; waitTime += 500; if(waitTime>=totalWaitTime) return false; } } catch (InterruptedException e) { throw new RuntimeException(e); } } }catch(Exception e){ throw new RuntimeException(e); }finally{ this.localLock.unlock(); } } public void unlock() { boolean getlocallock = this.localLock.tryLock(); if(!getlocallock) return; //判断当前对象是不是持有锁 if(!this._reallyLockId.equals(this.curLockId)){ this.localLock.unlock(); return; } try{ //解锁对象,尝试删除对象 this.zkClient.deletePath(this.getLockPath()); }catch(Exception e){ throw new RuntimeException(e); }finally{ this.localLock.unlock(); } } public Condition newCondition() { // TODO Auto-generated method stub return null; } private String getLockPath(){ if(lockPath==null) lockPath = ZKPATH+APP_NAME+"/"+lockId; return lockPath; }}
0 0
- 基于ZOOKEEPER的一个实现了Lock接口的分布式锁
- 基于zookeeper的分布式lock实现
- 基于zookeeper的分布式lock实现
- 基于zookeeper实现的分布式锁
- 基于zookeeper实现的分布式锁
- 基于zookeeper的分布式锁实现
- 基于zookeeper实现的分布式读写锁
- 基于zookeeper的分布式锁实现
- 基于Zookeeper的分布式锁实现
- 基于zookeeper的分布式锁实现
- ZooKeeper实战(五)基于zookeeper的分布式锁实现
- 分布式锁实现方式一 基于zookeeper的分布式锁
- 基于Zookeeper的分布式锁
- 基于ZooKeeper的分布式锁
- 基于ZooKeeper的分布式Session实现
- 基于ZooKeeper的分布式Session实现
- 基于ZooKeeper的分布式Session实现
- 基于ZooKeeper的分布式Session实现
- Linux下的文件管理函数(不带I/O缓冲)2.0
- android编程取消标题栏方法(appcompat_v7、Theme.NoTitleBar)
- 复习--JS基础
- 非root用户执行java进程报错:fork: retry:资源暂时不可用
- 12.1类的定义和声明
- 基于ZOOKEEPER的一个实现了Lock接口的分布式锁
- 框架内部工作流程
- 配置Git for Mac
- JAVA数组的length属性
- poj2159 Ancient Cipher 水题
- uoj 246. 【UER #7】套路
- Python爬虫利器之Selenium的用法
- 一种基于注解的Spring MVC权限控制方法
- Linux 系统的常用命令总结