理解实现zookeeper分布式锁

zookeeper为什么能实现分布式锁
a. zookeeper的数据一致性(paxos)
b. zookeeper的临时顺序节点
c. zookeeper的监听watch机制
zookeeper实现原理

   1. 所有尝试获取分布式锁的线程,会去zookeeper下一个持久化锁节点下面去创建临时的顺序节点。 

   2. 线程得到持久化节点的子节点(所有创建的临时顺序节点),排序,找到最小的节点,并判断是否是本线程创建的节点。
    2.1  是 \>  获取到分布式锁
    2.2  否 \>  2.2.1 .如果线程需要立即返回获取锁的结果。则返回失败  
    ------ -  -  2.2.2  如果线程一直等待直到获取锁。则监听本线程节点的排序前一个节点的节点删除事件(删除事件触发则回到第2步),线程阻塞。
   3. 释放锁:删除本线程节点

流程图
这里假设锁根节点是lock ;

这里写图片描述

明白实现的原理及流程就差不多写出了,其他就是一些细节的问题了。

个人简单实现:
锁接口:

public interface  DistributedLock {
    /**
     * 释放锁
     */
     void unlock();

    /**
     * 尝试获得锁,能获得就立马获得锁,如果不能获得就立马返回
     */
     boolean tryLock();

    /**
     * 尝试获得锁,一直阻塞,直到获得锁为止
     */
     void lock();
}

锁实现类:

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;

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 com.smart.mvc.util.StringUtils;

/**
 * 
 * @author james
 */
public class ZookeeperDistributedLock implements DistributedLock {

    /** session超时时间 */
    private final int SESSION_TIMEOUT = 5000;
    /** 持久化锁根节点 */
    private String root = "/lock-";
    /** 用于阻塞锁阻塞线程 */
    private CountDownLatch countDownLatch = new CountDownLatch(1);
    private ZooKeeper zookeeper;
    /** 本线程创建的节点总路径(包括跟路径) */
    private String lockPath;
    /** 异常集合 */
    private List<Exception> exception = new ArrayList<Exception>();

    /**
     * 连接zookeeper,并创建锁根节点
     * 
     * @param 连接地址
     */
    public ZookeeperDistributedLock(String address) {
        if (StringUtils.isBlank(address)) {
            throw new RuntimeException("Zookeeper address can not be empty !");
        }
        zookeeper = connectServer(address);
        if (zookeeper != null) {
            try {
                Stat stat = zookeeper.exists(root, false);
                if (stat == null) {
                    // 创建持久化节点
                    zookeeper.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                }
            } catch (KeeperException e) {
                exception.add(e);
            } catch (InterruptedException e) {
                exception.add(e);
            }

        }
    }

    /**
     * 连接zookeeper
     *
     * @param 连接地址
     * @return
     */
    private ZooKeeper connectServer(String address) {
        // 使用CountDownLatch阻塞线程确保zookeeper实例成功
        final CountDownLatch latch = new CountDownLatch(1);
        ZooKeeper zk = null;
        try {
            zk = new ZooKeeper(address, SESSION_TIMEOUT, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if (event.getState() == Event.KeeperState.SyncConnected) {
                        latch.countDown();
                    }
                }
            });
            latch.await();
        } catch (IOException e) {
            exception.add(e);
        } catch (InterruptedException ex) {
            exception.add(ex);
        }
        return zk;
    }

    /**
     * 一直等待获取锁
     * 
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void lock() {
        if (exception.size() > 0) {
            throw new RuntimeException(exception.get(0));
        }
        try {
            // 创建节点
            creatNode(root + "/lock_");
            // 尝试获取锁
            judgeLock();
            // 阻塞线程
            countDownLatch.await();
        } catch (Exception e) {
            throw new RuntimeException(e.getCause());
        }
    }

    /**
     * 判断是否能够拥有该锁
     * 
     * @throws Exception
     */
    private void judgeLock() throws Exception {
        try {
            workWithSortList(root, list -> {
                if (!lockPath.equals(root + "/" + list.get(0))) {
                    try {
                        waitForLock(list.get(Collections.binarySearch(list, lockPath.split("root/")[1]) - 1));
                    } catch (Exception e) {
                        throw new RuntimeException(e.getCause());
                    }
                } else {
                    countDownLatch.countDown();
                }
                return null;
            });
        } catch (Exception e) {
            throw new Exception(e.getCause());
        }
    }

    /**
     * 给前一个节点加上监听器
     * 
     * @param nodePath
     * @throws InterruptedException
     * @throws KeeperException
     */
    private void waitForLock(String nodePath) throws Exception {

        zookeeper.exists(root + "/" + nodePath, new Watcher() {
            // 节点删除时间
            @Override
            public void process(WatchedEvent event) {
                if (event.getType() == Event.EventType.NodeDeleted) {
                    try {
                        judgeLock();
                    } catch (Exception e) {
                        throw new RuntimeException(e.getCause());
                    }
                }
            }
        });
    }

    /**
     * 释放锁,并删除节点
     */
    public void unlock() {
        try {
            if (StringUtils.isNotBlank(lockPath)) {
                zookeeper.delete(lockPath, -1);
            }
        } catch (InterruptedException e) {
        } catch (KeeperException e) {
        }
    }

    /**
     * 尝试获得锁,能获得就立马获得锁并返回true,如果不能获得就立马返回false
     *
     * @return
     */
    public boolean tryLock() {
        try {
            creatNode(root + "/lock_");
            return workWithSortList(root, list -> {
                if (lockPath.equals(root + "/" + list.get(0))) {
                    return true;
                }
                return false;
            });
        } catch (Exception e) {
            throw new RuntimeException(e.getCause());
        }
    }

    /**
     * 获取到有序的子节点集合
     * 
     * @param rootpath
     * @param function
     * @return
     * @throws Exception
     */
    public <R> R workWithSortList(String rootpath, Function<List<String>, R> function) throws Exception {
        List<String> children = zookeeper.getChildren(rootpath, false);
        Collections.sort(children);
        if (children != null && !children.isEmpty()) {
            return function.apply(children);
        }
        return null;
    }

    /**
     * 创建临时有序节点
     * 
     * @param path
     * @return
     * @throws Exception
     */
    public String creatNode(String path) throws Exception {
        return lockPath = zookeeper.create(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值