Zookeeper(4)-使用ZooKeeper实现分布式锁

基于zookeeper临时有序节点可以实现的分布式锁。

大致思想即为:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

代码实现

  1. 分布式锁锁服务LockServer

import com.tc.zooker.config.ConnectWatcher;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class LockServer extends ConnectWatcher {
    private String rootPath = "/lock";
    private CountDownLatch countDownLatch;
    private String currentLock;
    public LockServer() {
        this.connect();
    }

    @Test
    public void createRoot() {
        try {
            this.zooKeeper.create(rootPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        super.process(watchedEvent);
        if(watchedEvent.getType()== Event.EventType.NodeDeleted){
            if(countDownLatch!=null){
                countDownLatch.countDown();
            }
        }
    }

    public void lock(CallBack callBack) {
        if(tryLock()){
            doAction(callBack);
        }else{
            waitLock();
            doAction(callBack);
        }

    }
    //获取锁的进程执行业务操作,来调用回调函数
    private void doAction(CallBack callBack) {
        callBack.lockEnd();
    }
    //元素等待获取锁,使用countDownLatch来阻塞当前线程的运行
    private void waitLock() {
        countDownLatch=new CountDownLatch(1);
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public boolean tryLock() {
        Long id = this.zooKeeper.getSessionId();
        String path = "lock-" + id;

        try {
            if (!isExist(path)) {
              currentLock=this.zooKeeper.create(rootPath+"/"+path+"_", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            }
            List<String> locks=this.zooKeeper.getChildren(rootPath,false);
            Collections.sort(locks);
            //判断当前znode是不是最小的一个
            if(locks.get(0).equals(currentLock.substring(currentLock.lastIndexOf("/")+1))){
                return true;
            }else{
                List<String> lockNames=new ArrayList<>();
                locks.forEach(lock->{
                    lockNames.add(lock.split("_")[0]);
                });
                int index=Collections.binarySearch(lockNames,path);
                //监听比当前元素小的元素的变化,来通知等待的元素来获取锁
                Stat stat = this.zooKeeper.exists(rootPath + "/" + locks.get(index - 1), this);
                    if(stat!=null){
                        return false;
                    }

                return true;
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
    //解决连接失败重试,造成死锁的问题
    public boolean isExist(String path) {
        List<String> lockNames = new ArrayList<String>();
        List<String> locks = null;
        try {
            locks = this.zooKeeper.getChildren(rootPath, false);
            locks.forEach((lock) -> {
                lockNames.add(lock.split("-")[0]);
            });
            int index = Collections.binarySearch(lockNames, path);
            if (index >-1) {
                currentLock=rootPath+"/"+locks.get(index);
                return true;
            }

        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    //解锁
    public void unLock() {
        try {
            this.zooKeeper.delete(currentLock,-1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
}
  1. 利用接口来实现回调函数CallBack

public interface CallBack {
    void lockEnd();
}
  1. 我们需要加锁的方法 LockClient

public class LockClient {
    public void testLock(int id){
        LockServer lockServer=new LockServer();
        lockServer.lock(new CallBack() {
            @Override
            public void lockEnd() {
                //处理自己的业务逻辑
                try {
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("编号"+id+"获取到分布式锁");

                lockServer.unLock();
            }
        });
    }
  1. 并发线程测试工具类LockThread

import java.util.concurrent.CountDownLatch;

public class LockThread  implements Runnable{
    CountDownLatch countDownLatch;
    int id;
    public LockThread(CountDownLatch countDownLatch,int id){
        this.countDownLatch=countDownLatch;
        this.id=id;
    }
    public void test() {
        Thread thread=new Thread(this);
        thread.start();
    }

    @Override
    public void run() {
        try {
            countDownLatch.await();
            LockClient lockClient=new LockClient();
            lockClient.testLock(id);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 测试类

import java.util.concurrent.CountDownLatch;

public class Main  {

    public static void main(String[] args) {
        CountDownLatch countDownLatch=new CountDownLatch(20);
        for (int i=0;i<20;i++){
            new LockThread(countDownLatch,i+1).test();
            countDownLatch.countDown();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章