• 分布式锁的实现【基于ZooKeeper】


    引言

    ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    ZooKeeper的架构通过冗余服务实现高可用性。因此,如果第一次无应答,客户端就可以询问另一台ZooKeeper主机。ZooKeeper节点将它们的数据存储于一个分层的命名空间,非常类似于一个文件系统或一个前缀树结构。客户端可以在节点读写,从而以这种方式拥有一个共享的配置服务。更新是全序的。

    基于ZooKeeper分布式锁的流程

    • 在zookeeper指定节点(locks)下创建临时顺序节点node_n
    • 获取locks下所有子节点children
    • 对子节点按节点自增序号从小到大排序
    • 判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
    • 若监听事件生效,则回到第二步重新进行判断,直到获取到锁

    具体实现

    下面就具体使用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();
            }
        }
    }

    运行结果:

    总体来说,如果了解到整个实现流程,使用zookeeper实现分布式锁并不是很困难,不过这也只是一个简单的实现,与前面实现Redis实现相比,本实现的稳定性更强,这是因为zookeeper的特性所致,在外界看来,zookeeper集群中每一个节点都是一致的。

    完整代码可以在我的GitHub中查看:https://github.com/hongmoshui/DistributedLock

    原文链接:https://www.cnblogs.com/liuyang0/p/6800538.html

  • 相关阅读:
    HDOJ 1207 汉诺塔II
    [转]写代码的小女孩
    POJ Subway tree systems
    HDOJ 3555 Bomb (数位DP)
    POJ 1636 Prison rearrangement (DP)
    POJ 1015 Jury Compromise (DP)
    UVA 10003
    UVA 103 Stacking Boxes
    HDOJ 3530 Subsequence
    第三百六十二、三天 how can I 坚持
  • 原文地址:https://www.cnblogs.com/hongmoshui/p/10599190.html
Copyright © 2020-2023  润新知