三、Zookeeper客户端API

三、Zookeeper客户端API

1、环境搭建
  • 创建一个maven项目导入zookeeper的坐标即可

    
    <dependency>
        <groupId>org.apache.zookeepergroupId>
        <artifactId>zookeeperartifactId>
        <version>3.5.7version>
    dependency>
    
  • 我是用了Junit5的测试

2、创建Zookeeper客户端
//表示连接的Zookeeper地址端口,,两端不能有空格
private String connect = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181";
//还要这里面不能直接使用ip地址:需要到windows的C:\Windows\System32\drivers\etc\hosts文件下配置地址映射
//否则就会出现连接不上的异常
//设置超时时间
private Integer timeout = 2000;
private ZooKeeper zkClient = null;
@BeforeEach//Junit5注解表示在测试方法执行前都执行
public void init() throws IOException {
   zkClient = new ZooKeeper(connect, timeout, new Watcher() {//Watcher : 监听函数
       @Override
       public void process(WatchedEvent event) {
			//监听代码
       }
   });
}
3、创建子节点
  • 测试代码

    //创建节点方法
    @Test
    public void createNode() throws Exception {
        String createNode = zkClient.create("/mnyuan", //参数1:要创建节点的路径
                "java".getBytes(), //参数2:节点数据
                ZooDefs.Ids.OPEN_ACL_UNSAFE, //参数3:节点权限
                CreateMode.PERSISTENT);//参数4:节点的类型
        System.out.println(createNode);
    }
    
  • 在本机上查看节点

    [zk: localhost:2181(CONNECTED) 0] ls /
    [mnyuan, zookeeper]
    
4、监听子节点
  • 使用之前的监听

        @BeforeEach//Junit5注解表示在测试方法执行前都执行
        public void init() throws IOException {
            zkClient = new ZooKeeper(connect, timeout, new Watcher() {//Watcher : 监听函数
                @Override
                public void process(WatchedEvent event) {
                    System.out.println("-------------------");
                    try {
                        List<String> children = zkClient.getChildren("/", true);
                        for (String child : children) {
                            System.out.println(child);
                        }
                    } catch (KeeperException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("-------------------");
                }
            });
        }
    
  • 监听测试

        //监听子节点
        @Test
        public void watchNode() throws KeeperException, InterruptedException {
            List<String> children = zkClient.getChildren("/", true);
            //使用true表示使用的之前创建的监听,参数二也可以自己编写一个监听
            for (String child : children) {
                System.out.println(child);
            }
            Thread.sleep(15000);//让该方法延迟一会,方法不能太早结束,
        }
    
5、判断节点是否存在
    //判断节点是否存在
    @Test
    public void exitsNode() throws KeeperException, InterruptedException {
        Stat stat = zkClient.exists("/mnyuan", false);//false : 不监听
        System.out.println(stat == null ? "node not exists" : "yes");
    }
6、客户端向服务端写数据的流程
  • 写流程的写入请求直接发给Leader节点

    三、Zookeeper客户端API_第1张图片

  • 写流程的写入请求发送给Follower节点

    三、Zookeeper客户端API_第2张图片

7、服务器动态上下线监听案例
  • 1、现在集群上创建/servers节点

    [zk: localhost:2181(CONNECTED) 4] create /servers
    Created /servers
    
  • 2、服务器端向Zookeeper注册的代码

    public class ZookeeperServer {
        private String connect = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181";
        private int timeOut = 2000;
        private ZooKeeper zK = null;
    
        public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
            ZookeeperServer server = new ZookeeperServer();
            //1. 获取连接
            server.connect();
            //2.注册服务器
            server.registServer(args[0]);
            //3.业务功能
            server.business();
        }
    
        private void business() throws InterruptedException {
            Thread.sleep(Long.MAX_VALUE);
        }
    
        private void registServer(String hostName) throws KeeperException, InterruptedException {
            String servers = zK.create("/servers/" + hostName, hostName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
            System.out.println(servers + "on line");
        }
    
        private void connect() throws IOException {
            zK = new ZooKeeper(connect, timeOut, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
    
                }
            });
        }
    }
    
  • 3、进行测试

    使用main方法传递参数,注意注意观察控制台参数

  • 2、服务器上下线,客户端监听的代码

    public class ZookeeperClient {
        private String connect = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181";
        private int timeOut = 2000;
        private ZooKeeper zk = null;
        public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
            ZookeeperClient client = new ZookeeperClient();
            //1.获取连接
            client.connect();
            //2.获取服务器列表信息
            client.getServerList();
            //3.业务逻辑
            client.business();
        }
    
        private void business() throws InterruptedException {
            Thread.sleep(Long.MAX_VALUE);
        }
    
        private void getServerList() throws KeeperException, InterruptedException {
            //监听/servers节点
            List<String> children = zk.getChildren("/servers", true);
            //servers用来保存节点信息
            List<String> servers = new ArrayList<>();
            for (String child : children) {
                //获取节点信息
                byte[] data = zk.getData("/servers/" + child, false, null);
                if (data != null){
                    servers.add(data.toString());
                }
            }
            //打印节点列表
            System.out.println(servers);
        }
    
        private void connect() throws IOException {
            zk = new ZooKeeper(connect, timeOut, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    try {
                        //只有再次调用才能监听
                        getServerList();
                    } catch (KeeperException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
    
  • 3、测试,注意观察控制台打印情况

    [zk: localhost:2181(CONNECTED) 13] create -e -s /servers/zookeeper01 "zookeeper01"
    Created /servers/zookeeper010000000001
    [zk: localhost:2181(CONNECTED) 14] create -e -s /servers/zookeeper02 "zookeeper02"
    Created /servers/zookeeper020000000002
    [zk: localhost:2181(CONNECTED) 15] delete /servers/zookeeper010000000001
    
8、Zookeeper分布式锁

三、Zookeeper客户端API_第3张图片

  • 原生代码实现

    public class DistributedLock {
    
        private final String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
        private final int sessionTimeout = 2000;
        private final ZooKeeper zk;
    
        private CountDownLatch connectLatch = new CountDownLatch(1);
        private CountDownLatch waitLatch = new CountDownLatch(1);
    
        private String waitPath;
        private String currentMode;
    
        public DistributedLock() throws IOException, InterruptedException, KeeperException {
            // 获取连接
            zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    // connectLatch  如果连接上zk  可以释放
                    if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
                        connectLatch.countDown();
                    }
                    // waitLatch  需要释放
                    if (watchedEvent.getType()== Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)){
                        waitLatch.countDown();
                    }
                }
            });
            // 等待zk正常连接后,往下走程序
            connectLatch.await();
            // 判断根节点/locks是否存在
            Stat stat = zk.exists("/locks", false);
            if (stat == null) {
                // 创建一下根节点
                zk.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        }
        // 对zk加锁
        public void zklock() {
            // 创建对应的临时带序号节点
            try {
                currentMode = zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
                // wait一小会, 让结果更清晰一些
                Thread.sleep(10);
                // 判断创建的节点是否是最小的序号节点,如果是获取到锁;如果不是,监听他序号前一个节点
                List<String> children = zk.getChildren("/locks", false);
                // 如果children 只有一个值,那就直接获取锁; 如果有多个节点,需要判断,谁最小
                if (children.size() == 1) {
                    return;
                } else {
                    Collections.sort(children);
                    // 获取节点名称 seq-00000000
                    String thisNode = currentMode.substring("/locks/".length());
                    // 通过seq-00000000获取该节点在children集合的位置
                    int index = children.indexOf(thisNode);
                    // 判断
                    if (index == -1) {
                        System.out.println("数据异常");
                    } else if (index == 0) {
                        // 就一个节点,可以获取锁了
                        return;
                    } else {
                        // 需要监听  他前一个节点变化
                        waitPath = "/locks/" + children.get(index - 1);
                        zk.getData(waitPath,true,new Stat());
                        // 等待监听
                        waitLatch.await();
                        return;
                    }
                }
            } catch (KeeperException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 解锁
        public void unZkLock() {
            // 删除节点
            try {
                zk.delete(this.currentMode,-1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (KeeperException e) {
                e.printStackTrace();
            }
        }
    }
    
  • 测试

    public class DistributedLockTest {
        public static void main(String[] args) throws InterruptedException, IOException, KeeperException {
           final  DistributedLock lock1 = new DistributedLock();
           final  DistributedLock lock2 = new DistributedLock();
           new Thread(new Runnable() {
               @Override
               public void run() {
                   try {
                       lock1.zklock();
                       System.out.println("线程1 启动,获取到锁");
                       Thread.sleep(5 * 1000);
                       lock1.unZkLock();
                       System.out.println("线程1 释放锁");
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock2.zklock();
                        System.out.println("线程2 启动,获取到锁");
                        Thread.sleep(5 * 1000);
                        lock2.unZkLock();
                        System.out.println("线程2 释放锁");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
    
9、使用Curator框架实现分布式锁案例
  • 1》原生的javaAPI开发存在的问题

    • 1、会话连接是异步的,需要自己去处理,比如使用CountDownLatch
    • 2、Watch需要重复注册,不然就不能生效
    • 3、开发的复杂性较高
    • 不支持多节点删除和创建。需要自己去递归
  • 2》Curator是一个专门解决分布式锁的框架,解决原生javaAPI开发分布式遇到的问题

  • 官方文档 :Apache Curator

  • 使用演示

    • 1、导入maven坐标

      
              <dependency>
                  <groupId>org.apache.curatorgroupId>
                  <artifactId>curator-frameworkartifactId>
                  <version>4.3.0version>
              dependency>
              <dependency>
                  <groupId>org.apache.curatorgroupId>
                  <artifactId>curator-recipesartifactId>
                  <version>4.3.0version>
              dependency>
              <dependency>
                  <groupId>org.apache.curatorgroupId>
                  <artifactId>curator-clientartifactId>
                  <version>4.3.0version>
              dependency>
      
    • 2、编写测试

      public class ZookeeperLocks {
          public static void main(String[] args) {
              //创建分布式锁1 : lock1
              InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");
              //创建分布式锁2 : lock2
              InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");
      
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      try {
                          lock1.acquire();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                      System.out.println("线程1 获取到锁");
                  }
              }).start();
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      try {
                          lock2.acquire();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                      System.out.println("线程2 获取到锁");
                  }
              }).start();
          }
      
          public static CuratorFramework getCuratorFramework(){
              ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);
              CuratorFramework client = CuratorFrameworkFactory
                      .builder()
                      .connectString("zookeeper01:2181,zookeeper02:2182,zookeeper03:2181")
                      .connectionTimeoutMs(2000)
                      .sessionTimeoutMs(2000)
                      .retryPolicy(policy).build();
              client.start();//启动客户端
              System.out.println("Zookeeper启动成功");
              return client;
          }
      }
      

你可能感兴趣的:(Zookeeper,zookeeper,java,分布式)