ZooKeeper使用案例

1、配置中心案例

工作中有这样的一个场景: 数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存。

若数据库的用户名和密码改变时候,还需要重新加载缓存,比较麻烦,通过ZooKeeper可以轻松完成,当数据库发生变化时自动完成配置同步。

设计思路:

  1. 连接zookeeper服务器
  2. 读取zookeeper中的配置信息,注册watcher监听器,存入本地变量
  3. 当zookeeper中的配置信息发生变化时,通过watcher的回调方法捕获数据变化事件
  4. 重新获取配置信息
public class MyConfigCenter implements Watcher {

    //  zk的连接串
    String IP = "127.0.0.1:2181";
    //  计数器对象
    static CountDownLatch countDownLatch = new CountDownLatch(1);
    // 连接对象
    static ZooKeeper zooKeeper;

    // 用于本地化存储配置信息
    private static String url;
    private static String username;
    private static String password;

    @Override
    public void process(WatchedEvent event) {
        // 捕获事件状态
        if (event.getType() == Event.EventType.None) {
            if (event.getState() == Event.KeeperState.SyncConnected) {
                System.out.println("连接成功");
                countDownLatch.countDown();
            } else if (event.getState() == Event.KeeperState.Disconnected) {
                System.out.println("连接断开!");
            } else if (event.getState() == Event.KeeperState.Expired) {
                System.out.println("连接超时!");
            } else if (event.getState() == Event.KeeperState.AuthFailed) {
                System.out.println("验证失败!");
            }
            // 当配置信息发生变化时
        } else if (event.getType() == EventType.NodeDataChanged) {
            initValue();
        }
    }

    // 读取配置信息
    public static void initValue(){
        try {
            url = new String(zooKeeper.getData("/config/url", true, null));
            username = new String(zooKeeper.getData("/config/username", true, null));
            password = new String(zooKeeper.getData("/config/password", true, null));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, new MyConfigCenter());
            initValue();
            // 阻塞线程,等待连接的创建成功
            countDownLatch.await();
            MyConfigCenter myConfigCenter = new MyConfigCenter();
            for (int i = 1; i <= 20; i++) {
                Thread.sleep(10000);
                System.out.println("url:"+myConfigCenter.getUrl());
                System.out.println("username:"+myConfigCenter.getUsername());
                System.out.println("password:"+myConfigCenter.getPassword());
                System.out.println("########################################");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
	//。。。省略getter/setter方法
}

2、生成分布式唯一ID

在过去的单库单表型系统中,通常可以使用数据库字段自带的auto_increment属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法在依靠数据库的auto_increment属性来唯一标识一条记录了。此时我们就可以用zookeeper在分布式环境下生成全局唯一ID。

设计思路:

  1. 连接zookeeper服务器
  2. 指定路径生成永久有序节点
  3. 取序列号及为分布式环境下的唯一ID
public class GloballyUniqueId implements Watcher {
    //  zk的连接串
    static String IP = "127.0.0.1:2181";
    //  计数器对象
    static CountDownLatch countDownLatch = new CountDownLatch(1);
    //  用户生成序号的节点
    String defaultPath = "/uniqueId";
    //  连接对象
    static ZooKeeper zooKeeper;

    @Override
    public void process(WatchedEvent event) {
        // 捕获事件状态
        if (event.getType() == Watcher.Event.EventType.None) {
            if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
                System.out.println("连接成功");
                countDownLatch.countDown();
            } else if (event.getState() == Watcher.Event.KeeperState.Disconnected) {
                System.out.println("连接断开!");
            } else if (event.getState() == Watcher.Event.KeeperState.Expired) {
                System.out.println("连接超时!");
            } else if (event.getState() == Watcher.Event.KeeperState.AuthFailed) {
                System.out.println("验证失败!");
            }
        }
    }

    // 生成id的方法
    public String getUniqueId() throws Exception {
        String path = "";

        //创建持久化有序节点
        path = zooKeeper.create(defaultPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);

        // /uniqueId0000000001
        return path.substring(9);
    }

    public static void main(String[] args) throws Exception {
        //打开连接
        zooKeeper = new ZooKeeper(IP, 5000, new GloballyUniqueId());
        // 阻塞线程,等待连接的创建成功
        countDownLatch.await();
        GloballyUniqueId globallyUniqueId = new GloballyUniqueId();
        for (int i = 1; i <= 20; i++) {
            String id = globallyUniqueId.getUniqueId();
            System.out.println(id);
            Thread.sleep(3000);
        }
    }
}

3、分布式锁

分布式锁有多种实现方式,比如通过数据库、redis都可实现。作为分布式协同工具ZooKeeper,当然也有着标准的实现方式。下面介绍在zookeeper中如何实现排他锁。

设计思路:

  1. 每个客户端往/Locks下创建临时有序节点/Locks/Lock_,创建成功后/Locks下面会有每个客户端对应的节点,如/Locks/Lock_000000001
  2. 客户端取得/Locks下子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功
  3. 如果自己的锁节点不在第一位,则监听自己前一位的锁节点。例如,自己锁节点Lock_000000002,那么则监听Lock_000000001
  4. 当前一位锁节点Lock_000000001对应的客户端执行完成,释放了锁。将会触发监听客户端(Lock_000000002)的逻辑
  5. 监听客户端重新执行第2步逻辑,判断自己是否获得了锁

4、服务注册中心

在 zookeeper 中,进行服务注册,实际上就是在 zookeeper 中创建了一个 znode 节点,该节点存储了该服务的ip、端口、调用方式(协议、序列化方式)等。该节点由服务提供者(发布服务时)创建,以供服务消费者获取节点中的信息;

  1. 服务提供者启动时,会将其服务名称,ip地址注册到配置中心;
  2. 服务消费者在第一次调用服务时,会通过注册中心找到相应服务的ip地址列表,并缓存到本地,以供后续使用;
  3. 当服务提供者的某台服务器宕机或下线时,相应的ip会从服务提供者ip列表中移除。同时,注册中心会将新的服务ip地址列表发送给服务消费者机器,缓存在消费者本机;
  4. 当某个服务的所有服务器都下线了,那么这个服务也就下线了;
  5. 同样,当服务提供者的某台服务器上线时,注册中心会将新的服务ip地址列表发送给服务消费者机器,缓存在消费者本机

zookeeper 集群搭建

单机环境下,jdk、zookeeper 安装完毕,进行zookeeper伪集群搭建。zookeeper集群中包含3个节点,节点对外提供服务端口号分别为2181、2182、2183

  1. 复制三份zookeeper文件
  2. zookeeper 最大会话超时 zookeeper state change_分布式

  3. 修改zookeeper2181对应配置文件
#服务器对应端口号
clientPort=2181
#数据快照文件所在路径
dataDir=E:/apache-zookeeper-3.6.2-bin-0/data
dataLogDir=E:/apache-zookeeper-3.6.2-bin-0/log
#集群配置信息
#server.A=B:C:D
#A:是一个数字,表示这个是服务器的编号
#B:是这个服务器的ip地址
#C:Zookeeper服务器之间的通信端口
#D:Leader选举的端口
server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883
  1. 在上一步 dataDir 指定的目录下,创建 myid 文件(没有后缀名),该文件只要一个数字,就是上一步server 配置的对应 A 数字
  2. zookeeper2182、zookeeper2183参照步骤2/3进行相应配置
  3. 分别启动三台服务器(启动三个安装包的 bin 下的 zkServer.cmd),检验集群状态

注意:一开始启动第一个会一直报错,不管它,都启动成功就不报错了;

一致性协议zab协议(二阶段提交)

zab协议的全称是 Zookeeper Atomic Broadcast (zookeeper原子广播)。zookeeper 是通过 zab协议来保证分布式事务的最终一致性;

zookeeper 最大会话超时 zookeeper state change_System_02

zab广播模式工作原理,主要分为以下几个步骤:

  1. leader从客户端收到一个写请求(如果是follower收到写请求则会转发给leader)
  2. leader生成一个新的事务并为这个事务生成一个唯一的ZXID
  3. leader将这个事务提议(propose)发送给所有的follows节点
  4. follower节点将收到的事务请求加入到历史队列(history queue)中,并发送ack给leader
  5. 当leader收到大多数follower(半数以上节点)的ack消息,leader会发送commit请求
  6. 当follower收到commit请求时,从历史队列中将事务请求commit

ACK (Acknowledge character):即是确认字符,接收方发给发送方的,表示发来的数据已确认接收无误!

zookeeper的leader选举

服务器状态

服务器状态总共分为四种:

  • looking:寻找leader状态。当服务器处于该状态时,它会认为当前集群中没有leader,因此需要进入leader选举状态
  • leading: 领导者状态。表明当前服务器角色是leader
  • following: 跟随者状态。表明当前服务器角色是follower
  • observing:观察者状态。表明当前服务器角色是observer

服务器启动时期的leader选举

在集群初始化阶段,当有一台服务器server1启动时,其单独无法进行和完成leader选举,当第二台服务器server2启动时,此时两台机器可以相互通信,每台机器都试图找到leader,于是进入leader选举过程。选举过程如下:

  1. 每个server发出一个投票。由于是初始情况,server1和server2都会将自己作为leader服务器来进行投票,每次投票会包含所推举的服务器的myid和zxid,使用(myid, zxid)来表示,此时server1的投票为(1, 0),server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器;
  2. 集群中的每台服务器接收来自集群中各个服务器的投票;
  3. 处理投票,针对每一个投票,服务器都需要将别人的投票和自己的投票进行pk,pk规则如下:
优先检查zxid,zxid比较大的服务器优先作为leader。
如果zxid相同,那么就比较myid。myid较大的服务器作为leader服务器。

示例:
对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的zxid,均为0,再比较myid,此时server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票。
对于server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
  1. 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于server1、server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了leader;
  2. 改变服务器状态。一旦确定了leader,每个服务器就会更新自己的状态,如果是follower,那么就变更为following,如果是leader,就变更为leading;

服务器运行时期的Leader选举

在zookeeper运行期间,leader与非leader服务器各司其职,即便当有非leader服务器宕机或新加入,此时也不会影响leader,但是一旦leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮leader选举,其过程和启动时期的Leader选举过程基本一致。

假设正在运行的有server1、server2、server3三台服务器,当前leader是server2,若某一时刻leader挂了,此时便开始Leader选举。选举过程如下:

  1. 变更状态。leader挂后,余下的服务器都会将自己的服务器状态变更为looking,然后开始进入leader选举过程。
  2. 每个server会发出一个投票。在运行期间,每个服务器上的zxid可能不同,此时假定server1的zxid为122,server3的zxid为122,在第一轮投票中,server1和server3都会投自己,产生投票(1, 122),(3, 122),然后各自将投票发送给集群中所有机器。
  3. 接收来自各个服务器的投票。与启动时过程相同
  4. 处理投票。与启动时过程相同,此时,server3将会成为leader。
  5. 统计投票。与启动时过程相同。
  6. 改变服务器的状态。与启动时过程相同。

observer角色及其配置

observer角色特点:

  1. 不参与集群的leader选举
  2. 不参与集群中写数据时的ack反馈

要使用observer角色,在任何想变成observer角色的配置文件中加入如下配置:

peerType=observer

并在所有server的配置文件中,配置server的那行追加:observer,例如:

server.3=192.168.60.130:2289:3389:observer

zookeeperAPI连接集群

ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)
  • connectionString - zooKeeper集合主机。
  • sessionTimeout - 会话超时(以毫秒为单位)。
  • watcher - 实现“监视器”界面的对象。ZooKeeper集合通过监视器对象返回连接状 态。

zookeeper 开源客户端curator介绍

curator是Netflix公司开源的一个zookeeper客户端,curator框架在zookeeper原生API接口上进行了包装,解决了很多zooKeeper客户端非常底层的细节开发。提供zooKeeper各种应用场景的抽象封装,实现了Fluent风格的API接口,是最好用,最流行的zookeeper的客户端。

原生zookeeperAPI的不足:

  • 连接对象异步创建,需要开发人员自行编码等待
  • 连接没有自动重连超时机制
  • watcher一次注册生效一次
  • 不支持递归创建树形节点

curator特点:

  • 解决session会话超时重连
  • watcher反复注册
  • 简化开发api
  • 遵循Fluent风格的API
  • 提供了分布式锁服务、共享计数器、缓存机制等机制

前言:使用异步方式操作的话,那么取数据一定要在异步回调里取。

1、连接到ZooKeeper

public class CuratorConnection {
    public static void main(String[] args) {
        // session重连策略
        /*
            3秒后重连一次,只重连1次
            RetryPolicy retryPolicy = new RetryOneTime(3000);
        */
        /*
            每3秒重连一次,重连3次
            RetryPolicy retryPolicy = new RetryNTimes(3,3000);
        */
       /*
            每3秒重连一次,总等待时间超过10秒后停止重连
            RetryPolicy retryPolicy=new RetryUntilElapsed(10000,3000);
       */
        /**
            随着重连的次数增加,重连的间隔会变长,重连间隔基于此公式计算出来
            baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1)))
         */
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);

        // 创建连接对象
        CuratorFramework client= CuratorFrameworkFactory.builder()
                // IP地址端口号
                .connectString("127.0.0.1:2181")
                // 会话超时时间
                .sessionTimeoutMs(5000)
                // 重连机制
                .retryPolicy(retryPolicy)
                // 命名空间,可以不传
                .namespace("create")
                // 构建连接对象
                .build();
        // 打开连接
        client.start();
        System.out.println(client.isStarted());
        // 关闭连接
        client.close();
    }
}

2、新增节点

public class CuratorCreate {
    @Test
    public void create1() throws Exception {
        //自定义权限列表
        List<ACL> list = new ArrayList<ACL>();
        // 授权模式和授权对象
        Id id = new Id("ip", "127.0.0.1");
        list.add(new ACL(ZooDefs.Perms.ALL, id));

        // 新增节点
        client.create()
                // 递归创建节点树
                .creatingParentsIfNeeded()
                // 节点的类型
                .withMode(CreateMode.PERSISTENT)
                // 节点的权限列表 world:anyone:cdrwa
                .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                //.withACL(list)
                // 异步回调接口
                .inBackground(new BackgroundCallback() {
                    public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
                        // 事件类型
                        System.out.println(curatorEvent.getType());
                    }
                })
                // arg1:节点的路径,arg2:节点的数据
                .forPath("/node1/node11", "node1".getBytes());
    }
}

3、更新节点

public class CuratorSet {
    @Test
    public void set1() throws Exception {
        // 更新节点
        client.setData()
                // 指定版本号
                .withVersion(2)
                // 异步方式修改节点数据
				inBackground((curatorFramework, curatorEvent) -> {
                    // 事件的类型
                    System.out.println(curatorEvent.getType());
                })
                // arg1:节点的路径,arg2:节点的数据
                .forPath("/node1", "node11".getBytes());
    }
}

4、删除节点

public class CuratorDelete {
    @Test
    public void delete1() throws Exception {
       // 删除节点
        client.delete()
                // 删除包含字节点的节点
                .deletingChildrenIfNeeded()
                // 版本号
                .withVersion(0)
                // 异步方式删除节点
                .inBackground((curatorFramework, curatorEvent) -> {
                    // 事件类型
                    System.out.println(curatorEvent.getType());
                })
                // 节点的路径
                .forPath("/node1");
    }
}

5、查看节点

public class CuratorGet {
    @Test
    public void get1() throws Exception {
        // 读取数据时读取节点的属性
        Stat stat=new Stat();
        // 读取节点数据
        byte [] bys=client.getData()
                // 读取属性
                .storingStatIn(stat)
                 // 节点的路径
                .forPath("/node1");
        System.out.println(new String(bys));
        System.out.println(stat.getVersion());
    }
    
    @Test
    public void get2() throws Exception {
        // 异步方式读取节点的数据
        client.getData()
                 .inBackground((curatorFramework, curatorEvent) -> {
                     // 节点的路径
                     System.out.println(curatorEvent.getPath());
                     // 事件类型
                     System.out.println(curatorEvent.getType());
                     // 数据
                     System.out.println(new String(curatorEvent.getData()));
                 })
                .forPath("/node1");
        Thread.sleep(5000);
    }
}

6、 查看子节点

public class CuratorGetChild {
    @Test
    public void getChild1() throws Exception {
        // 读取子节点数据
        List<String> list = client.getChildren()
                // 节点路径
                .forPath("/get");
        for (String str : list) {
            System.out.println(str);
        }
    }

    @Test
    public void getChild2() throws Exception {
        // 异步方式读取子节点数据
        client.getChildren()
                .inBackground((curatorFramework, curatorEvent) -> {
                    // 节点路径
                    System.out.println(curatorEvent.getPath());
                    // 事件类型
                    System.out.println(curatorEvent.getType());
                    // 读取子节点数据
                    List<String> list=curatorEvent.getChildren();
                    for (String str : list) {
                        System.out.println(str);
                    }
                })
                .forPath("/get");
        Thread.sleep(5000);
    }
}

8、检查节点是否存在

public class CuratorExists {
    @Test
    public void exists1() throws Exception {
        // 判断节点是否存在
       Stat stat= client.checkExists()
                 // 节点路径
                .forPath("/node2");
        System.out.println(stat.getVersion());
    }

    @Test
    public void exists2() throws Exception {
        // 异步方式判断节点是否存在
        client.checkExists()
                 .inBackground((curatorFramework, curatorEvent) -> {
                     // 节点路径
                     System.out.println(curatorEvent.getPath());
                     // 事件类型
                     System.out.println(curatorEvent.getType());
                     System.out.println(curatorEvent.getStat().getVersion());
                 })
                .forPath("/node2");
        Thread.sleep(5000);
    }
}

9、watcherAPI

curator提供了两种Watcher(Cache)来监听结点的变化:

  • Node Cache : 只是监听某一个特定的节点,监听节点的新增和修改
  • PathChildren Cache : 监控一个ZNode的子节点. 当一个子节点增加, 更新,删除时, Path Cache会改变它的状态,会包含最新的子节点,子节点的数据和状态
public class CuratorWatcher {
    @Test
    public void watcher1() throws Exception {
        // 监视某个节点的数据变化
        // arg1:连接对象,arg2:监视的节点路径
       final NodeCache nodeCache=new NodeCache(client,"/watcher1");
        // 启动监视器对象
        nodeCache.start();
        nodeCache.getListenable().addListener(()->{
            System.out.println(nodeCache.getCurrentData().getPath());
            System.out.println(new String(nodeCache.getCurrentData().getData()));
        });
        Thread.sleep(100000);
        //关闭监视器对象
        nodeCache.close();
    }

    @Test
    public void watcher2() throws Exception {
         // 监视子节点的变化
        // arg1:连接对象,arg2:监视的节点路径,arg3:事件中是否可以获取节点的数据
        PathChildrenCache pathChildrenCache=new PathChildrenCache(client,"/watcher1",true);
        // 启动监听
        pathChildrenCache.start();
        // 当子节点方法变化时回调的方法
        pathChildrenCache.getListenable().addListener((curatorFramework, pathChildrenCacheEvent) -> {
            // 节点的事件类型
            System.out.println(pathChildrenCacheEvent.getType());
            // 节点的路径
            System.out.println(pathChildrenCacheEvent.getData().getPath());
            // 节点数据
            System.out.println(new String(pathChildrenCacheEvent.getData().getData()));
        });
        Thread.sleep(100000);
        // 关闭监听
        pathChildrenCache.close();
    }
}

10、事务

public class CuratorTransaction {
    @Test
    public void tra1() throws Exception {
        // 开启事务
        client.inTransaction()
                .create().forPath("/node1","node1".getBytes())
                .and()
                .create().forPath("/node2","node2".getBytes())
                .and()
                //事务提交
                .commit();
    }
}

11、分布式锁

  • InterProcessMutex:分布式可重入排它锁(在这一时刻只能有一个线程进入)
  • InterProcessReadWriteLock:分布式读写锁(在同一时刻可以允许多个读线程访问,但是在写线程访问的时候,所有的读线程和其他写线程都会被阻塞)
public class CuratorLock {
    @Test
    public void lock1() throws Exception {
        // 排他锁
        // arg1:连接对象
        // arg2:节点路径
        InterProcessLock interProcessLock = new InterProcessMutex(client, "/lock1");
        System.out.println("等待获取锁对象!");
        // 获取锁
        interProcessLock.acquire();
        for (int i = 1; i <= 10; i++) {
            Thread.sleep(3000);
            System.out.println(i);
        }
        // 释放锁
        interProcessLock.release();
        System.out.println("等待释放锁!");
    }

    @Test
    public void lock2() throws Exception {
        // 读写锁
        InterProcessReadWriteLock interProcessReadWriteLock=new InterProcessReadWriteLock(client, "/lock1");
        // 获取读锁对象
        InterProcessLock interProcessLock=interProcessReadWriteLock.readLock();
        // 获取写锁对象
        //InterProcessLock interProcessLock=interProcessReadWriteLock.writeLock();
        System.out.println("等待获取锁对象!");
        // 获取锁
        interProcessLock.acquire();
        for (int i = 1; i <= 10; i++) {
            Thread.sleep(3000);
            System.out.println(i);
        }
        // 释放锁
        interProcessLock.release();
        System.out.println("等待释放锁!");
    }
}

zookeeper四字监控命令

zooKeeper支持某些特定的四字命令与其的交互。它们大多是查询命令,用来获取 zooKeeper服务的当前状态及相关信息。用户在客户端可以通过 telnet 或 nc 向zooKeeper提交相应的命令。 zooKeeper常用四字命令见下表 所示:

命令

描述

conf

输出相关服务配置的详细信息。比如端口、zk数据及日志配置路径、最大连接数,session超时时间、serverId等

cons

列出所有连接到这台服务器的客户端连接/会话的详细信息。包括“接受/发送”的包数量、session id 、操作延迟、最后的操作执行等信息

crst

重置当前这台服务器所有连接/会话的统计信息

dump

列出未经处理的会话和临时节点

envi

输出关于服务器的环境详细信息

ruok

测试服务是否处于正确运行状态。如果正常返回"imok",否则返回空

stat

输出服务器的详细信息:接收/发送包数量、连接数、模式(leader/follower)、节点总数、延迟。 所有客户端的列表

srst

重置server状态

wchs

列出服务器watches的简洁信息:连接总数、watching节点总数和watches总数

wchc

通过session分组,列出watch的所有节点,它的输出是一个与 watch 相关的会话的节点列表

mntr

列出集群的健康状态。包括“接受/发送”的包数量、操作延迟、当前服务模式(leader/follower)、节点总数、watch总数、临时节点总数

nc命令工具安装,下载地址:https://eternallybored.org/misc/netcat/

zookeeper 最大会话超时 zookeeper state change_服务器_03


四字命令使用方法:echo 命令| nc localhost 2181,如果执行提示如下信息:

conf is not executed because it is not in the whitelist.

需要在zoo_sample.cfg或zoo.cfg(看你使用的哪个)配置文件末尾添加如下一行代码并保存退出:

4lw.commands.whitelist=*

重启zookeeper即可!

注意:解压就可以使用,如果报病毒,在隔离区恢复文件就行!

1、conf命令

输出相关服务配置的详细信息

shell终端输入:echo conf| nc localhost 2181(后续命令都不再重复赘述)

属性

含义

clientPort

客户端端口号

dataDir

数据快照文件目录 默认情况下100000次事务操作生成一次快照

dataLogDir

事物日志文件目录,生产环境中放在独立的磁盘上

tickTime

服务器之间或客户端与服务器之间维持心跳的时间间隔(以毫秒为单位)

maxClientCnxns

最大连接数

minSessionTimeout

最小session超时 minSessionTimeout=tickTime*2

maxSessionTimeout

最大session超时 maxSessionTimeout=tickTime*20

serverId

服务器编号

initLimit

集群中的follower服务器(F)与leader服务器(L)之间初始连接时能容忍的最多心跳数

syncLimit

集群中的follower服务器(F)与leader服务器(L)之间 请求和应答之间能容忍的最多心跳数

electionAlg

0:基于UDP的LeaderElection 1:基于UDP的FastLeaderElection 2:基于UDP和认证的FastLeaderElection 3:基于TCP的FastLeaderElection 在3.4.10版本中,默认值为3另外三种算法已经被弃用,并且有计划在之后的版本中将它们彻底删除而不再支持

electionPort

选举端口

quorumPort

数据通信端口

peerType

是否为观察者 1为观察者

2、cons命令

列出所有连接到这台服务器的客户端连接/会话的详细信息

属性

含义

ip

ip地址

port

端口号

queued

等待被处理的请求数,请求缓存在队列中

received

收到的包数

sent

发送的包数

sid

会话id

lop

最后的操作 GETD-读取数据 DELE-删除数据 CREA-创建数据

est

连接时间戳

to

超时时间

lcxid

当前会话的操作id

lzxid

最大事务id

lresp

最后响应时间戳

llat

最后/最新 延时

minlat

最小延时

maxlat

最大延时

avglat

平均延时

3、crst命令

重置当前这台服务器所有连接/会话的统计信息

4、dump命令

列出未经处理的会话和临时节点

属性

含义

session id

znode path(1对多 , 处于队列中排队的session和临时节点)

5、envi命令

输出关于服务器的环境配置信息

属性

含义

zookeeper.version

版本

host.name

host信息

java.version

java版本

java.vendor

供应商

java.home

运行环境所在目录

java.class.path

classpath

java.library.path

第三方库指定非java类包的位置(如:dll,so)

java.io.tmpdir

默认的临时文件路径

java.compiler

JIT 编译器的名称

os.name

Linux

os.arch

amd64

os.version

3.10.0-514.el7.x86_64

user.name

zookeeper

user.home

/home/zookeeper

user.dir

/home/zookeeper/zookeeper2181/bin

6、ruok命令

测试服务是否处于正确运行状态

7、stat命令

输出服务器的详细信息与srvr相似,但是多了每个连接的会话信息

属性

含义

Zookeeper version

版本

Latency min/avg/max

延时

Received

收包

Sent

发包

Connections

连接数

Outstanding

堆积数

Zxid

最大事物id

Mode

服务器角色

Node count

节点数

8、srst命令

重置server状态

9、wchs命令

列出服务器watches的简洁信息

属性

含义

connectsions

连接数

watch-paths

watch节点数

watchers

watcher数量

10、wchc命令
通过session分组,列出watch的所有节点,它的输出的是一个与 watch 相关的会话的节点列表

11、wchp命令

通过路径分组,列出所有的 watch 的session id信息

12、mntr命令

列出服务器的健康状态

属性

含义

zk_version

版本

zk_avg_latency

平均延时

zk_max_latency

最大延时

zk_min_latency

最小延时

zk_packets_received

收包数

zk_packets_sent

发包数

zk_num_alive_connections

连接数

zk_outstanding_requests

堆积请求数

zk_server_state

leader/follower 状态

zk_znode_count

znode数量

zk_watch_count

watch数量

zk_ephemerals_count

临时节点(znode)

zk_approximate_data_size

数据大小

zk_open_file_descriptor_count

打开的文件描述符数量

zk_max_file_descriptor_count

最大文件描述符数量

zookeeper图形化的客户端工具(ZooInspector)

ZooInspector下载地址:

https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

解压后进入目录ZooInspector\build,运行zookeeper-dev-ZooInspector.jar

#执行命令如下
java -jar zookeeper-dev-ZooInspector.jar

zookeeper 最大会话超时 zookeeper state change_zookeeper 最大会话超时_04


zookeeper 最大会话超时 zookeeper state change_分布式_05

总结

深入学习zookeeper(一)