2、zookeeper安装、常用命令、API说明及简单示列(创建ZK会话及对节点进行操作、创建分布式锁、创建全局唯一ID)

1.本文搭建的环境是zookeeper 3.5.8

  网友分享的安装教程当前已经很是详细了,在此就不做分享了。如有遇问题,请移步 window中ZK环境搭建ZK伪集群环境搭建   或留言。在对ZK有了一定的了解之后,再花篇章详解ZK的基本配置、存储配置、网络配置和调优,个人感觉这样效果会更好一些,因为只是讲解配置实在是有点枯燥,一种干巴巴的感觉。

2.环境搭建完成之后就可以进行命令练习

2.1 对节点操作的相关命令:
2.1.1 创建节点语法:create [-s] [-e] [-c] [-t ttl] path [data] [acl]
创建节点参数:
-s:顺序节点
-e:临时节点
-c:
-t ttl:
path:节点路径
data:节点数据
acl:节点权限

示列1:创建持久节点 create /workers worker-1
示例2:创建临时节点 create -e /tempPath tempValue
示例3:创建顺序节点 create -s /seqPath seqValue
Created /seqPath0000000011

示列4:创建firstZnode下的子节点child1、child2
create /firstZnode/child1 "firstchildren"
create /firstZnode/child2 "secondchildren"
说明:创建永久节点/workers,节点数据为worker-1

2.1.2 设置节点数据语法:set [-s] [-v version] path data
设置节点数据参数:
-s:
-v version:
示列: set /workers worker-5
说明:设置workers节点的数据为worker-5。设置节点值时,如果已经存在则覆盖掉原来值

2.1.3 获取节点数据语法:get [-s] [-w] path
-s:获取节点数据,额外列出节点stat信息
-w:获取监视点
示列1:get /workers
示列2:get seqPath0000000011 获取顺序节点
示列3:get -w /firstZnode 通过get命令设置监视点
[zk: localhost:2181(CONNECTED) 43] get -w /firstZnode
myFirstZookeeper-app
[zk: localhost:2181(CONNECTED) 44] set /firstZnode firstZnodeValue
[zk: localhost:2181(CONNECTED) 45]
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/firstZnode
备注:当指定的znode或znode的子数据更改时,监视器会显示通知。你只能在 get 命令中设置watch。

2.1.4 删除节点语法: delete [-v version] path
deleteall path [-b batch size]
条件删除节点
-b batch size
示列1: delete /firstZnode 删除节点
示列1: deleteall /firstZnode 删除节点

2.1.5 列出节点目录语法(只列出一层目录):ls [-s] [-w] [-R] path
-s:列出节点目录,额外列出节点stat信息
-w:设置对应子节点变化的监视
示列1:
[zk: localhost:2181(CONNECTED) 27] ls /firstZnode
[child1, child2]

示列2:通过 ls -w 可以设置对应znode 的子节点变化的监视点
ls -w /tasks

示列3:通过stat命令设置监视点
[zk: localhost:2181(CONNECTED) 2] stat /master true
'stat path [watch]' has been deprecated. Please use 'stat [-w] path' instead. deprecated(及其不赞成,强烈反对)
cZxid = 0xd
ctime = Wed Aug 26 14:15:43 GMT+08:00 2020
mZxid = 0xd
mtime = Wed Aug 26 14:15:43 GMT+08:00 2020
pZxid = 0xd
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 0

[zk: localhost:2181(CONNECTED) 3] stat -w /master
cZxid = 0xd
ctime = Wed Aug 26 14:15:43 GMT+08:00 2020
mZxid = 0xd
mtime = Wed Aug 26 14:15:43 GMT+08:00 2020
pZxid = 0xd
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 0

2.1.6 列出节点信息语法: stat [-w] path
示列:
[zk: localhost:2181(CONNECTED) 17] stat /workers
cZxid = 0xa //节点创建时的zxid
ctime = Wed Aug 26 14:13:30 GMT+08:00 2020 //节点创建时间
mZxid = 0x3a //节点最近一次更新时的zxid
mtime = Wed Aug 26 18:01:22 GMT+08:00 2020 //节点最近一次更新的时间
pZxid = 0xa
cversion = 0 //子节点数据更新次数
dataVersion = 8 //本节点数据更新次数
aclVersion = 0 //节点ACL(授权信息)的更新次数
ephemeralOwner = 0x0 //如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是临时节点,ephemeralOwner值为0
dataLength = 8 //节点数据长度
numChildren = 0 //子节点个数

2.2 配额相关命令:
2.2.1 设置节点配额信息语法:setquota -n|-b val path
-n:子节点最大数量
-b:节点最大长度
示列: setquota -n 50 /workers

2.2.2 显示节点设置的配额信息语法:listquota path
示列:listquota /workers
absolute path is /zookeeper/quota/workers/zookeeper_limits
Output quota for /workers count=50,bytes=-1
Output stat for /workers count=1,bytes=8

2.2.3 删除节点的配额信息语法:delquota [-n|-b] path
示列:delquota /workers

2.3 其他命令:
  2.3.1 zkCli.sh 启动客户端
  2.3.1 quit 退出会话
  2.3.3 zkServer.sh start 启动zk服务
  2.3.4 zkServer.sh stop 关闭zk服务

3.结合API及代码示列开始ZK的简单使用:创建ZK会话创建节点、获取所有节点、设置节点上的数据、判断节点是否存在

 添加如下依赖:

       <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
        </dependency>
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>
View Code

3.1创建ZK会话、创建节点、获取所有节点、设置节点上的数据、判断节点是否存在的代码示例

/*
 * @Copyright (C),
 * @ClassName: ZkSimpleOperateDemo
 * @Author: student
 * @Date: 2020/8/26 12:00
 * @Description:zk的简单操作:创建节点、获取所有节点、设置节点上的数据、判断节点是否存在
 * @History:
 * @Version:1.0
 */
public class ZkSimpleOperateDemo {
    private static String hostPort = "localhost:2181";
    private static int sessionTimeout = 50 * 1000;
    private static ZooKeeper zk;

    static {
        try {
            //创建Zookeeper连接对象
            zk = new ZooKeeper(hostPort, sessionTimeout, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws KeeperException, InterruptedException {
        //1.创建assign节点
        createZkNode(zk, "/assign", "任务列表");

        //2.获取所有子节点
        getAllChildren(zk);

        //3.设置节点上的数据
        setZkNodeData(zk, "/workers", "worker-1");

        //4.获取节点上数据值
        getZkNodeData(zk, "/workers");

        //5.判断节点是否存在
        isExitZKPath(zk, "/workers");

    }

    /**
     * @Method:
     * @Author:
     * @Description: 获取所有节点信息
     * param:
     * @Return:
     * @Exception:
     * @Date: 2020/8/26 14:30
     */
    public static void getAllChildren(ZooKeeper zk) {
        List<String> zooChildren = new ArrayList<String>();
        if (zk != null) {
            try {
                String zpath = "/";
                zooChildren = zk.getChildren(zpath, false);
                System.out.println("Znodes of '/': ");
                for (int i = 0; i < zooChildren.size(); i++) {
                    System.out.println("节点" + i + "-->" + zooChildren.get(i));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建节点
     * 1. CreateMode.PERSISTENT :持久节点,一旦创建就保存到硬盘上面
     * 2. CreateMode.SEQUENTIAL : 顺序持久节点
     * 3. CreateMode.EPHEMERAL :临时节点,创建以后如果断开连接则该节点自动删除
     * 4. CreateMode.EPHEMERAL_SEQUENTIAL :顺序临时节点
     * @Method:
     * @Author: student
     * @Description:
     * @param zooKeeper Zookeeper已经建立连接的对象
     * @param path      要创建节点的路径
     * @param data      该节点上的数据
     * @Return:返回创建的节点的路径
     * @Exception:
     * @Date: 2020/8/26 17:16
     */
    public static String createZkNode(ZooKeeper zooKeeper, String path, String data) throws KeeperException, InterruptedException {
        byte[] bytesData = data.getBytes();
        //访问控制列表
        ArrayList<ACL> openAclUnsafe = ZooDefs.Ids.OPEN_ACL_UNSAFE;
        //创建模式
        CreateMode mode = CreateMode.EPHEMERAL;
        String result = zooKeeper.create(path, bytesData, openAclUnsafe, mode);
        System.out.println("创建节点成功: " + result);
        return result;
    }

    /**
     * @Method:
     * @Author: student
     * @Description:设置节点上的数据 param:
     * @Return:
     * @Exception:
     * @Date: 2020/8/26 14:57
     */
    public static Stat setZkNodeData(ZooKeeper zooKeeper, String path, String data) throws KeeperException, InterruptedException {
        return zooKeeper.setData(path, data.getBytes(), -1);
    }

    /**
     * @Method:
     * @Author: student
     * @Description:获取节点上的数据
     * param:
     * @Return:
     * @Exception:
     * @Date: 2020/8/26 17:14
     */
    public static String getZkNodeData(ZooKeeper zooKeeper, String path) throws KeeperException, InterruptedException {
        byte[] data=zooKeeper.getData(path, true, new Stat());
        System.out.println(path+">>>节点上的值为");
        return new String(data);
    }

    /**
     * @Method:
     * @Author: student
     * @Description:判断节点是否存在
     * param:
     * @Return:
     * @Exception:
     * @Date: 2020/8/26 15:02
     */
    public static Stat isExitZKPath(ZooKeeper zooKeeper, String path) throws KeeperException, InterruptedException {
        Stat stat = zooKeeper.exists(path, false);
        System.out.println("stat-->"+stat);
        return stat;
    }
View Code
ZooKeeper 对象API:
1) 构造器:
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)
Watcher:客户端使⽤Watcher接口来监控与ZooKeeper之间会话的健康情况。与ZooKeeper服务器之间建⽴或失去连接时就会产⽣事件。它们同样还能⽤于监控ZooKeeper数据的变化。最终,如果与ZooKeeper的会话过期,也会通过Watcher接⼜传递事件来通知应用客户端

2) 获取znode节点数据的字节数组
byte[] getData(String path, boolean watch, Stat stat) throws KeeperException, InterruptedException{}
path:我们想要获取数据的 znode节点路径
watch:我们是否想要监听后续的数据变更,设置为true,我们就可以通过我们创建ZooKeeper句柄时所设置的Watcher对象得到事件,同时另⼀个版本的⽅法提供了以Watcher对象为⼊参,通过这个传⼊的对象来接收变更的事件。
stat:getData⽅法会填充znode节点的元数据信息,Stat结构包括znode节点的属性信息,如该znode点的上次更新(zxid)的时间戳,以及该znode节点的⼦节点数

3) 获取节点数据:
public byte[] getData(String path, boolean watch, Stat stat) throws KeeperException, InterruptedException{}
path:节点路径
watch:是否需要进行监视该节点的变化,如果需要的话设置为true,否则为false
stat:通过Stat结构 可以获得当前主节点成立时间等信息
getData⽅法并不会进⾏任何硬盘的访问,但却需要依赖⽹络传输
4) 获取子节点
public List<String> getChildren(String path, boolean watch) throws KeeperException, InterruptedException{}
 5) 判断节点是否存在
  public Stat exists(String path, boolean watch) throws KeeperException,InterruptedException{}

 3.2 通过zk创建分布式锁

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class ZkDistributeLockTest {
 
    public static void main(String[] args) {
        // 使用CountDownLunch控制线程同时执行
        CountDownLatch countDownLatch = new CountDownLatch(1);
        // 开启3个线程模拟分布式环境,分布式环境下每个进程都是一个单独的zkClient
        Thread t1 = new Thread(new CompeteThread(countDownLatch));
        Thread t2 = new Thread(new CompeteThread(countDownLatch));
        Thread t3 = new Thread(new CompeteThread(countDownLatch));
        t1.start();
        t2.start();
        t3.start();
 
        System.out.println("休眠3秒后执行..." + System.currentTimeMillis());
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 倒计时结束
        countDownLatch.countDown();
    }
}

import org.I0Itec.zkclient.ZkClient;

import java.util.concurrent.CountDownLatch;

/*
 * @Copyright (C), 2002-2020,
 * @ClassName: CompeteThread
 * @Author:
 * @Date: 2020/10/28 22:55
 * @Description:
 * @History:
 * @Version:1.0
 */
public class CompeteThread implements Runnable {
    // 共享变量
    private static Integer CNT = 0;
    private ZkClient zkClient;
    private CountDownLatch countDownLatch;

    private static String hostPort = "localhost:2181";

    public CompeteThread(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    // zk初始化
    private void connect() {
        String threadName = Thread.currentThread().getName();
        try {
            System.out.println(threadName + " 等待执行...");
            // 等待倒计时结束
            countDownLatch.await();
            System.out.println(threadName + " 请求连接zk..." + System.currentTimeMillis());
            zkClient = new ZkClient(hostPort, 20000);
            System.out.println(threadName + " 连接成功...");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        // 初始化连接(在各个线程里开启连接,模拟分布式环境)
        connect();
        String threadName = Thread.currentThread().getName();

        // 竞争锁
        while (true) {
            try {
                System.out.println(threadName + " 开始竞争锁...");
                // 创建zk临时节点
                zkClient.createEphemeral("/dl", "test");
                System.out.println(threadName + " 获得锁!!!");
                // 获得锁后修改共享变量
                CNT++;
                System.out.println(threadName + " 释放了锁..." + CNT);
                zkClient.delete("/dl");
                Thread.sleep(2000);
            } catch (Exception e) {
                // 创建临时节点失败,表示未获得锁
                System.out.println(threadName + " 未获得锁,将重试!!!");
//                System.out.println(e.getMessage());
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

}
View Code

 3.3 通过zk创建全局唯一ID:通过持久顺序节点实现

package com.zj.weblearn.zk.intodoor;

import org.apache.zookeeper.*;

import java.util.concurrent.CountDownLatch;

public class GloballyUniqueId {

    private static String defaultPath = "/unique_id";
    private String myId = "my_";
    private String connectString = "127.0.0.1:2181";

    private ZooKeeper zooKeeper;

    //ZK初始化
    public GloballyUniqueId() {
        try {
            init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getUniqueId() {
        return myId + getPathId();
    }

    private String getPathId() {
        String path = "";
        try {
            // 创建一个临时顺序节点
            path = zooKeeper.create(this.defaultPath, "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        } catch (Exception e) {
            e.printStackTrace();
        }
//        return path.toLowerCase().replaceAll("[^a-z|0-9]", "");
        return path.toLowerCase().replaceAll("/", "");
    }

    private void init() throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        //创建ZK对象
        this.zooKeeper = new ZooKeeper(this.connectString, 5000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                //如果没有创建成功,则进行等待
                if (Event.KeeperState.SyncConnected == event.getState()) {
                    countDownLatch.countDown();
                }
            }
        });
        countDownLatch.await();
        System.out.println("=======zk init success========");
    }


    public static void main(String[] args) throws InterruptedException {
        GloballyUniqueId globallyUniqueId = new GloballyUniqueId();
        //2147483647   21亿条
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            System.out.println(globallyUniqueId.getUniqueId());
        }
    }
}
View Code

 3.4 通过zk创建全局唯一ID:通过节点版本号实现

package com.zj.weblearn.zk.intodoor;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

public class ZkIdGenerator {

    private ZooKeeper zk;
    private String path;

    public ZkIdGenerator(String serverAddress, String path) {
        try {
            this.path = path;
            zk = new ZooKeeper(serverAddress, 3000, event -> {
                System.out.println(event.toString());
            });

            if (zk.exists(path, false) == null) {
                zk.create(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (IOException | InterruptedException | KeeperException e) {
            e.printStackTrace();
        }
    }

    public long next() {
        try {
            Stat stat = zk.setData(path, new byte[0], -1);
            return stat.getVersion();
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
        return -1;
    }

    public static void main(String[] args) {
        ZkIdGenerator zkIdGenerator = new ZkIdGenerator("localhost:2181", "/id-gen");
        for (int i = 0; i < 1000; i++) {
            System.out.println(zkIdGenerator.next());
        }
    }
}
View Code

3.5 获取节点的相关运行状态

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.Date;

/*
 * @Copyright (C),
 * @ClassName: Demo2_node_manager
 * @Author:
 * @Date: 2020/8/30 15:14
 * @Description: 编写你⾃⼰的管理客户端;以便更快更简单地管理系统。在本例中,我们通过getData和getChildren⽅法来获得主从系统的运⾏状态。
 *
 * @History:
 * @Version:1.0
 */
public class Demo2_node_manager {
    /**
     * @Method:
     * @Author:
     * @Description: 获取master、workers、Tasks节点下子节点列表信息
     * param:
     * @Return:
     * @Exception:
     * @Date: 2020/9/7 16:58
     */
    public static void queryNodeStateAndValue(String nodePath) throws KeeperException {
        ZooKeeper zk = ZkUtils.startZk(null);
        try {
            Stat stat = new Stat();
            /*
            由于在/master节点中保存了主节点名称信息,因此我们可以从/master中获取数据,以获得当前主节点的名称,因为我们并不关⼼变化情况,所以将getData()方法中的第⼆个参数置为false。
            我们通过Stat结构,可以获得当前主节点成为主节点的时间信息、创建时间(ctime)即该znode节点建立时的秒数(系统纪元以来的秒数,即自1970年1月1日00:00:00UTC的描述)
            */
            byte masterData[] = zk.getData(nodePath, false, stat);
            Date startDate = new Date(stat.getCtime());
            System.out.println("节点 " + nodePath + " since " + startDate+"创建;数据值:"+ new String(masterData) );
        } catch (KeeperException.NoNodeException e) {
            System.out.println(String.format("节点 ",nodePath,"不存在"));
        } catch (InterruptedException e) {
            System.out.println(e.toString());
        }

    }

    public static void queryNodeChildrens(String nodePath) throws KeeperException {
        ZooKeeper zk = ZkUtils.startZk(null);
        System.out.println(nodePath);
        try {
            for (String w : zk.getChildren(nodePath, false)) {
                byte data[] = zk.getData(nodePath+"/" + w, false, null);
                System.out.println("\t" + w + ": " + new String(data));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public static void main(String args[]) throws Exception {
        queryNodeStateAndValue("/master");
        queryNodeChildrens("/workers");
    }

}
View Code

 3.6 竞争主节点

public class Demo3_compete_master_node{
    String masterflag = "masterflag";
    static boolean isLeader = false;
    static ZooKeeper zk;
    static{
        zk = ZkUtils.startZk(null);
    }

    /**
     * @Method: 
     * @Author:
     * @Description:
     * param: 判断是否为主节点
     * @Return: 
     * @Exception: 
     * @Date: 2020/8/29 16:26
     */
    boolean checkMaster(String nodePath) {
            try {
                Stat stat = new Stat();
                /*1.通过获取/master节点的数据来检查活动主节点*/
                byte data[] = zk.getData(nodePath, false, stat);

                /*2.该⾏展示了为什么我们需要使用在创建/master节点时保存的数据:
                如果/master存在,我们使用/master中的数据来确定谁是群首。如果⼀个进程捕获到ConnectionLossException,这个进程可能就是主节点,因create操作实际上已经处理完,但响应消息却丢失了。
                */
                isLeader = new String(data).equals(masterflag);
            } catch (KeeperException.NoNodeException e) {
                // no master, so try create again
            } catch (KeeperException | InterruptedException e) {

            }
            return isLeader;
    }

    /**
     * @Method: 
     * @Author:
     * @Description:
     * param: 主节点异常处理
     * @Return: 
     * @Exception: 
     * @Date: 2020/8/29 16:27
     */
    void runForMaster(String nodePath) throws InterruptedException {
            try {
                //如果create请求成功执⾏,则将会成为主节点
                String createNodePath=zk.create(nodePath, masterflag.getBytes(),OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                if(nodePath.equals(createNodePath)){
                    isLeader = true;
                }
                return;
            } catch (KeeperException.NodeExistsException e) {
                isLeader = false;
                return;
            } catch (KeeperException.ConnectionLossException e) {
                //处理ConnectionLossException异常的catch块的代码为空,因为我们并不想中⽌函数,这样就可以使处理过程继续向下执⾏。

            } catch (KeeperException e) {
                e.printStackTrace();
            }
            if (checkMaster(nodePath))  return;;
    }

    /*
        ConnectionLossException异常(ConnectionLossException异常为KeeperException异常的⼦类)发⽣于客户端与ZooKeeper服务端失去连,接时。⼀般常常由于⽹络原因导致,如⽹络分区或ZooKeeper服务器故障。
        当这个异常发⽣时,客户端并不知道是在ZooKeeper服务器处理前丢失了请求消息,还是在处理后客户端未收到响应消息。如我们之前所描述的,ZooKeeper的客户端库将会为后续请求重新建⽴连接,但进程必须知道⼀个未决请求是否已经处理了还是需要再次发送请求。
        InterruptedException异常源于客户端线程调⽤了Thread.interrupt,通常这是因为应⽤程序部分关闭,但还在被其他相关应⽤的⽅法使⽤。从字⾯来看这个异常,进程会中断本地客户端的请求处理的过程,并使该请求处于未知状态。
    */

    public static void main(String args[])throws Exception {
        Demo3_compete_master_node m = new Demo3_compete_master_node();
        //1.当前进程成为主节点或另⼀进程成为主节点后返回。
        m.runForMaster("/master");
        if(isLeader){
            //1.1 当应用程序创建主节点成功,我们在此处开始执⾏这些逻辑(输出我们成为主节点的信息,然后等待60秒后退出main函数)
            System.out.println("master node create success and now I'm the leader");
            Thread.sleep(6000);
        }else{
            System.out.println("Someone else is the leader");
        }
        zk.close();
    }

}
View Code

 3.6 异步节点操作

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;

import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;

/*
 * @Copyright (C),
 * @ClassName: Demo4_async_create_node
 * @Author:
 * @Date: 2020/8/29 16:44
 * @Description:异步获取管理权限:ZooKeeper中,所有同步调⽤⽅法都有对应的异步调⽤⽅法。通过异步调⽤,我们可以在单线程中同时进⾏多个调⽤,同时也可以简化我们的实现⽅式。
 * @History:
 * @Version:1.0
 */
public class Demo4_async_create_node{
    String masterflag = "masterflag";
    static boolean isLeader = false;
    static ZooKeeper zk;
    static{
        zk = ZkUtils.startZk(null);
    }

    AsyncCallback.StringCallback masterCreateCallback = new AsyncCallback.StringCallback() {
        @Override
        public void processResult(int rc, String path, Object ctx, String name) {
            System.out.println("异步创建主节点回调方法masterCreateCallback>>>执行,其中创建主节点的结果rc信息为>>>" + rc);
                /*
                rc参数中获得create请求的结果,并将其转换为Code枚举类型。rc如果不为0,则对应KeeperException异常。
                    场景1:如果因连接丢失导致create请求失败,我们会得到CONNECTIONLOSS编码的结果,⽽不是ConnectionLossException异常。
                    场景2:当连接丢失时,我们需要检查系统当前的状态,并判断我们需要如何恢复,我们将会在我们后面实现的checkMaster⽅法中进⾏处理。
                */
            switch (KeeperException.Code.get(rc)) {
                case CONNECTIONLOSS:
                    checkMaster(ctx.toString());
                case OK:
                    System.out.println(ctx.toString()+" create success and I'm the leader");
                    isLeader = true;
                    break;
                default:
                    isLeader = false;
            }

        }
    };;

    /*
     * 异步回调的方式创建主节点
     * */
    void runForMaster(String nodePath) {
        /*
            1.create⽅法的异步调⽤API:
                create(final String path, byte data[], List<ACL> acl, CreateMode createMode,  StringCallback cb, Object ctx)
                参数介绍:
                    AsyncCallback.StringCallback:为回调⽅法的对象
                    ctx:用户指定上下⽂信息(回调⽅法调用时候传⼊的对象实例)
            2.本示列说明:
                在runForMaster⽅法中,我们将masterCreateCallback传给create⽅法,传⼊null作为上下⽂对象参数
        */
        zk.create(nodePath, masterflag.getBytes(), OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, masterCreateCallback, nodePath);
    }




    void checkMaster(String nodePath) {
        try {
            byte[] masterInfo = zk.getData(nodePath, false, null);
            String masterFlag=new String(masterInfo);
            if(masterflag.equals(masterFlag)){
                //1.发生 CONNECTIONLOSS 异常,但是发现主节点创建成功了
                isLeader=true;
                return;
            }else{
                //2.主节点未创建成功.再次进行创建
                runForMaster(nodePath);
            }
        } catch (KeeperException e) {
            System.out.println("checkMaster1>>>"+e);
        } catch (InterruptedException e) {
            System.out.println("checkMaster2>>>"+e);
        }
    }


    public static void main(String args[]) throws Exception {
        Demo4_async_create_node m = new Demo4_async_create_node();

        //步骤1:异步创建主节点:当前进程成为主节点或另⼀进程成为主节点后返回。
        m.runForMaster("/master");

        //步骤2:其他业务逻辑执行
        System.out.println("与主节点创建无关的其他业务逻辑执行完成>>>");

        Thread.sleep(8000);

        /*
         *步骤3:执行异步创建主节点函数:AsyncCallback.StringCallback 用于获取节点的名称
         * */
        if (isLeader) {
            //4.自己创建主节点成功后,可执行的逻辑
            System.out.println("I create master node success, can do follow infos");
        } else {
            System.out.println("Someone else is the leader");
        }
        zk.close();
    }

}
View Code

 3.7 注册从节点

import org.apache.zookeeper.*;

import java.io.IOException;

/*
 * @Copyright (C),
 * @ClassName: Demo5_register_slave_node
 * @Author:
 * @Date: 2020/8/30 11:09
 * @Description:注册从节点
 * @History:
 * @Version:1.0
 */
public class Demo5_register_slave_node implements Watcher {
    ZooKeeper zk;

    String hostPort;
    String serverId;

    public Demo5_register_slave_node(String hostPort, String serverId) {
        this.hostPort = hostPort;
        this.serverId = serverId;
    }

    void startZK() throws IOException {
        zk = new ZooKeeper(hostPort, 15000, this);
    }

    @Override
    public void process(WatchedEvent watchedEvent) {

        System.out.println(watchedEvent.toString() + ", " + hostPort);

    }

    AsyncCallback.StringCallback createWorkerCallback = new AsyncCallback.StringCallback() {
        public void processResult(int rc, String path, Object ctx,
                                  String name) {
            switch (KeeperException.Code.get(rc)) {
                //因为这个进程是唯⼀创建表示该进程的临时性znode节点的进程,如果创建节点时连接丢失,进程会简单地重试创建过程。
                case CONNECTIONLOSS:
                    register();
                    break;
                case OK:
                    System.out.println("Registered successfully: " + serverId);
                    break;
                case NODEEXISTS:
                    System.out.println("Already registered: " + serverId);
                    break;
                default:
                    System.out.println("Something went wrong: "+ KeeperException.create(KeeperException.Code.get(rc), path));

            }
        }
    };


    void register() {
        zk.create("/workers/worker-" + serverId,
                "Idle".getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL,
                createWorkerCallback, null);
    }

    public static void main(String args[]) throws Exception {
        String stringHost="localhost:2181";
        String serverId="1";
        Demo5_register_slave_node w = new Demo5_register_slave_node(stringHost,serverId);
        w.startZK();
        w.register();

        Thread.sleep(10000);
    }
}
View Code

 3.7 有序任务列表

import org.apache.zookeeper.*;

import java.io.IOException;

import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;

/*
 * @Copyright (C),
 * @ClassName: Demo6_tasks_serialize
 * @Author:
 * @Date: 2020/8/30 14:25
 * @Description:
        Client应⽤程序队列化新任务,以便从节点执⾏这些任务,我们会在/tasks节点下添加⼦节点来表⽰需要从节点需要执⾏的命
   令。我们将会使⽤有序节点,这样做有两个好处,第⼀,序列号指定了任务被队列化的顺序;第⼆,可以通过很少的⼯作为任务创建基于序列号的唯⼀路径。

 * @History:
 * @Version:1.0
 */
public class Demo6_tasks_serialize implements Watcher {
    static ZooKeeper zk;

    String hostPort;

    public Demo6_tasks_serialize(String hostPort) {
        this.hostPort = hostPort;
    }

    void startZK() throws IOException {
        zk = new ZooKeeper(hostPort, 15000, this);
    }


    @Override
    public void process(WatchedEvent watchedEvent) {
        System.out.println(watchedEvent.toString() + ", " + hostPort);
    }

    public static String queueCommand(String command) throws KeeperException {
        while (true) {
            String name = "";
            try {
                name = zk.create("/tasks/task-",
                        command.getBytes(), OPEN_ACL_UNSAFE,
                        CreateMode.PERSISTENT_SEQUENTIAL);
                //我们⽆法确定使用CreateMode.SEQUENTIAL调用create的序列号,create⽅法会返回新建节点的名称。
                /*
                如果我们在执⾏create时遇到连接丢失,我们需要重试create操作。因为多次执⾏创建操作,也许会为⼀个任务建立多个znode节点,对于⼤多
                数⾄少执⾏⼀次(execute-at-least-once)策略的应用程序,也没什么问 题。对于某些最多执⾏⼀次(execute-at-most-once)策略的应用程序,我
                们就需要多⼀些额外⼯作:我们需要为每⼀个任务指定⼀个唯⼀的ID(如会话ID),并将其编码到znode节点名中,在遇到连接丢失的异常时,我
                们只有在/tasks下不存在以这个会话ID命名的节点时才重试命令。
                */
                return name;
            } catch (KeeperException.NodeExistsException e) {
                System.out.println(name + " already appears to be running");
            } catch (KeeperException.ConnectionLossException e) {


            } catch (InterruptedException e) {


            }
        }
    }

    public static void main(String args[]) throws Exception {
        String stringHost = "localhost:2181";
        Demo6_tasks_serialize serializeTasksNode = new Demo6_tasks_serialize(stringHost);
        serializeTasksNode.startZK();
        String name = queueCommand("2");
        System.out.println("Created " + name);
    }


}
View Code

 

参看资料:
https://www.jianshu.com/p/1f4c70d7ef40          
https://www.cnblogs.com/sun-flower1314/p/11385442.html demo示列
https://www.cnblogs.com/ydymz/p/9626653.html 搭建与使用
https://www.cnblogs.com/chunxiaozhang/p/12752944.html linux zk 安装
https://www.cnblogs.com/chunxiaozhang/p/12759595.html 命令大全
https://www.w3cschool.cn/zookeeper/             zk中文教程
apache-zookeeper-3.5.8-bin/docs/javaExample.html 安装包中自带的API
https://www.cnblogs.com/wuyizuokan/p/11241670.html 集群环境搭建
https://blog.csdn.net/rainyear/article/details/86293122 ZK生成全局唯一ID
https://www.cnblogs.com/zhya/p/11211458.html 分布式锁
posted @ 2020-09-04 20:20  爱笑的berg  阅读(353)  评论(0编辑  收藏  举报