zookeeper02

1.zookeeper作为配置中心

  在springboot中,我们可以创建配置类。把配置类的配置信息放入到zookeeper,使用watch监控,当zookeeper的node的数据发生变化时,事件触发,获取最新的数据,修改配置类配置信息

2.zookeeper生成分布式唯一id

  在过去的单库单表型系统中,通常可以使用数据库字段自带的auto_increment属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法在依靠数据库的auto_increment属性来唯一标识一条记录了。此时我们就可以用zookeeper在分布式环境下生成全局唯一ID。
  设计思路:
    1.连接zookeeper服务器
    2.指定路径生成有序节点
    3.取有序节点名称上的序列号就可以作为分布式环境下的唯一ID

    

     利用的是zookeeper的有序node的特性,它会给有序节点一个有序的序号,这个序号作为唯一id

3.zookeeper生成分布式锁

   在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。在单机环境中,Java中其实提供了很多并发处理相关的API,但是这些API在分布式场景中就无能为力了。也就是说单 纯的Java Api并不能提供分布式锁的能力。
​    分布式锁有多种实现方式,比如通过数据库、redis都可实现。作为分布式协同工具ZooKeeper,当然也有着标准的实现方式。下面介绍在zookeeper中如何实现排他锁。
    设计思路:

      利用zookeeper有序节点的生成会自动生成有序序号。当我要执行逻辑前,可以在一个node下去生成有序子节点,然后获取node下的子节点的列表,判断自己刚才生成的子节点是否是在第一位,如果是在第一位,代表获取了锁,执行逻辑,完成后删除刚才创建的子节点。否则就等待,并且监控排在前面的子节点被删除的事件,前一个节点被删除,释放锁了,取消等待,再判断自己是否排在第一位了,是,执行逻辑,释放锁。这样子就可以保证一个一个有序的执行了。执行顺序就是子节点的创建顺序。

      

      1.客户端往/Locks下创建临时有序节点/Locks/Lock
      2.客户端取得/Locks下子节点,并进行排序,判断排在最前面的是否为自己刚才创建的临时有序节点,如果自己刚才创建的锁节点在第一位,代表获取锁成功,执行逻辑完成,删除刚才创建的节点,完成锁的释放
      3.如果自己的锁节点不在第一位,判断前一位node是否还存在,不存在,直接重新从第二步开始执行。若存在,则等待前一位node的删除监听事件触发
      4.当前一位锁节点的逻辑执行完毕,释放锁了,再次从第二步开始执行,本节点再次判断是否获取锁

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class MyLock {
    
    String IP = "192.168.60.130:2181";
    //  计数器对象
    CountDownLatch countDownLatch = new CountDownLatch(1);
    //ZooKeeper配置信息
    ZooKeeper zooKeeper;
    private static final String LOCK_ROOT_PATH = "/Locks";
    private static final String LOCK_NODE_NAME = "Lock_";
    private String lockPath;
    

  // 构造方法中打开zookeeper连接     public MyLock() {         try {             zooKeeper new ZooKeeper(IP, 5000, new Watcher() {                 @Override                 public void process(WatchedEvent event) {                     if (event.getType() == Event.EventType.None) {                         if (event.getState() ==                  Event.KeeperState.SyncConnected) {                             System.out.println("连接成功!");                             countDownLatch.countDown();                         }                     }                 }             });             countDownLatch.await();         } catch (Exception ex) {             ex.printStackTrace();         }     }
    
//获取锁的方法     public void acquireLock() throws Exception {         //首先自己创建node对象对应设计思路中的第一步
         createLock();    
     
    //尝试获取锁   对应设计思路的2 3 4 5     
     attemptLock();    
     }
    
//创建锁节点     private void createLock() throws Exception {         //判断Locks是否存在,不存在创建         Stat stat = zooKeeper.exists(LOCK_ROOT_PATH, false);         if (stat == null) {             zooKeeper.create(LOCK_ROOT_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);         }         // 创建临时有序节点-返回的是新创建的临时节点的路径         lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);         System.out.println("节点创建成功:" + lockPath);     }



    
//监视器对象,监视上一个节点是否被删除,这个监视器在设计思路的第三步中用到     Watcher watcher = new Watcher() {         @Override         public void process(WatchedEvent event) {             if (event.getType() == Event.EventType.NodeDeleted) {                 synchronized (this) {                     notifyAll();                 }             }         }     };



    
//尝试获取锁     private void attemptLock() throws Exception {         // 获取Locks节点下的所有子节点 - 对应涉及思路第二步获取子节点         List<String> list = zooKeeper.getChildren(LOCK_ROOT_PATH, false);         // 对子节点进行排序 对应涉及思路第二步 对子节点进行排序         Collections.sort(list);         // /Locks/Lock_000000001         int index = list.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1)); //对应涉及思路第二步 lockPath.substring(LOCK_ROOT_PATH.length() + 1)是获取刚才创建的子节点的名称 list.indexOf是获取刚才创建的子节点在子节点列表中的排序         if (index == 0) { //对应涉及思路第二步 排在第一位,标识获取锁成功,可以执行             System.out.println("获取锁成功!");             return;         } else {  //对应涉及思路第三步 不在第一位,获取锁失败,监听子节点列表中前一位的节点             // 上一个节点的路径             String path = list.get(index ‐ 1);             Stat stat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + path, watcher);             if (stat == null) { //stat==null 表示前一位子节点没有了,再次调用本方法 对应设计思路第三步,判断前一个节点是否存在                 attemptLock();             } else { //若是前一位节点还存在,                 synchronized (watcher) {                     watcher.wait(); //等待前一位节点被删除,前一位节点被删除,会调用watcher 的porcess方法,process方法中调用noticeAll方法,wait结束,只需往下执行                 }                 attemptLock(); //对应设计思路第四 再次调用本方法             }         }     }     //释放锁     public void releaseLock() throws Exception {             //删除临时有序节点             zooKeeper.delete(this.lockPath,‐1);             zooKeeper.close();             System.out.println("锁已经释放:"+this.lockPath);     }   }
原文地址:https://www.cnblogs.com/jthr/p/14632030.html