zoukankan      html  css  js  c++  java
  • 【Zookeeper】Zookeeper实现分布式锁

    zookeeper实现分布式锁

    一:zookeeper分布式锁原理

    1:原子性

    Zookeeper有写操作有原子性,利用这个特性可以实现分布式锁。

    对于来自客户端的每个更新请求,ZooKeeper 具备严格的顺序访问控制能力。

    为了保证事务的顺序一致性,ZooKeeper 采用了递增的事务 id 号(zxid)来标识事务。

    2:Watcher 回调机制

    客户端注册监听它关心的 znode,当 znode 状态发生变化(数据变化、子节点增减变化)时,ZooKeeper 服务会通知客户端。

    客户端和服务端保持连接一般有两种形式:

    • 客户端向服务端不断轮询
    • 服务端向客户端推送状态

    Zookeeper 的选择是服务端主动推送状态,也就是观察机制( Watch )。

    二:实现分布式锁

    我们可以通过Zookeeper的临时节点和watcher机制实现分布式锁

    分布式锁

    1:实现tryLock

      public static ThreadLocal<CuratorZookeeperClient>  zkClient = null;
    
        public static String LOCK_NAME = "/lock";
    
        public static ThreadLocal<String> CURRENT_NODE = new ThreadLocal<>();
    
        static {
            zkClient.set(new CuratorZookeeperClient("localhost:8080",2000,2000,null,new RetryOneTime(1)));
        }
    
    
    @Override
        public boolean tryLock()  {
            String nodeName = LOCK_NAME + "/zk_";
            try {
                CURRENT_NODE.set(zkClient.get().getZooKeeper().create(nodeName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL));
    
                List<String> children = zkClient.get().getZooKeeper().getChildren(nodeName, false);
    
                Collections.sort(children);
    
                // 判断是否获得锁
                String minNode = children.get(0);
                if ((LOCK_NAME +"/"+minNode).equals(CURRENT_NODE.get())){
                    // 获取到锁
                    return true;
                }
                // 没有获取到锁,监听前一个节点是否存在
                // 获取前一个节点的index
              int i = children.indexOf(CURRENT_NODE.get().substring(CURRENT_NODE.get().lastIndexOf("/") + 1));
                // 获取前一个节点
              String preNodeName = children.get(i);
              // 增加countDownLatch 同步线程
              CountDownLatch countDownLatch = new CountDownLatch(1);
              zkClient.get().getZooKeeper().exists(LOCK_NAME + "/" + preNodeName, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                  // 监听前一个节点,前一个临时节点消失就是代表当前线程为最小的那个节点,可以获取到锁
                  if (Event.EventType.NodeDeleted.equals(watchedEvent.getType())){
                    countDownLatch.countDown();
                  }
                }
              });
               countDownLatch.await();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }
    

    二:解锁:删除节点就可以了

     @Override
        public void unlock() {
          try {
            zkClient.get().getZooKeeper().delete(CURRENT_NODE.get(),-1);
            CURRENT_NODE.remove();
          } catch (Exception e) {
            e.printStackTrace();
          }finally{
            zkClient.get().close();
          }
        }
    

    这只是个demo没有经过测试的,主要展示一下逻辑

    三:和Redis分布式锁的优劣势

    优点:不存在redis的超时、数据同步(zookeeper是同步完以后才返回)、主从切换(zookeeper主从切换的过程中服务是不可用的)的问题,可靠性很高。

    缺点:依赖中间件,保证了可靠性的同时牺牲了一部分效率(但是依然很高)。性能不如redis。

  • 相关阅读:
    泛函编程(29)-泛函实用结构:Trampoline-不再怕StackOverflow
    泛函编程(28)-粗俗浅解:Functor, Applicative, Monad
    泛函编程(27)-泛函编程模式-Monad Transformer
    泛函编程(26)-泛函数据类型-Monad-Applicative Functor Traversal
    泛函编程(25)-泛函数据类型-Monad-Applicative
    mac安装iterm2
    像使用linux一样使用mac
    springboot工程自动生成工具
    mysql insert返回主键
    linux rz sz命令
  • 原文地址:https://www.cnblogs.com/simple-flw/p/14825210.html
Copyright © 2011-2022 走看看