基于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
原创粉丝点击