0


zookeeper随堂笔记

学习目标:

  1. 什么是zookeeper
  2. 应用场景
  3. 基本的操作1. 安装部署2. shell客户端3. java端
  4. 基本原理 1. 选举机制2. 数据一致性3. 数据的读写流程

1 zookeeper简介

zookeeper是一个底层的集群协调工具,(比如:NN和DN之间的状态感应;监控 通知)!

具备基本的功能有 ,记录用户的状态数据 (写), 返回用户的数据(读) ,监控和通知。zookeeper为了高可用和安全性是一个集群! 3台(每个节点的数据完全一致)!

2 应用场景

  • 写数据
  • 读取数据
  • 监控通知

2.1 服务状态感知

2.2 分布式锁

2.3 分布式配置同步

2.4 统一域名

分布式中统一CMD指令

分布式中服务器的状态感知

3 操作

3.1 安装

zookeeper在部署的时候选择奇数台!要求zk集群半数以上的机器在线才能正常工作!

**上传解压 **

配置

  1. 在zk安装目录下 创建文件夹 zkData mkdir zkData /opt/apps/zookeeper-3.4.6/zkData
  2. 修改conf/下的配置模板配置文件名 mv zoo_sample.cfg zoo.cfg
  3. vi zoo.cfg dataDir=/opt/apps/zookeeper-3.4.6/zkData# Set to "0" to disable auto purge feature#autopurge.purgeInterval=1server.1=linux01:2888:3888server.2=linux02:2888:3888server.3=linux03:2888:3888
  4. 在zkData文件夹下创建myid文件 echo 1 > zkData/myid

**同步安装包 **

scp -r  zookeeper-3.4.6/  linux02:$PWD
scp -r  zookeeper-3.4.6/  linux03:$PWD

修改各个节点myid的值 linux02【2】 linux03【3】

**启动集群 **

在各个节点上启动ZK服务

/opt/apps/zookeeper-3.4.6/bin/zkServer.sh  start

查看进程 jps

[root@linux01 bin]# jps
1697 QuorumPeerMain
1720 Jps

查看集群状态 在每个节点执行

/opt/apps/zookeeper-3.4.6/bin/zkServer.sh  status

-------------------------------------------------------
Mode: follower

Mode: leader

Mode: follower

3.2 编写一键启动脚本

#!/bin/bash
# zk启停脚本 
for hostname in linux01  linux02  linux03
do 
echo "连接${hostname}... ...正在执行 $1"
ssh ${hostname} "source  /etc/profile ;/opt/apps/zookeeper-3.4.6/bin/zkServer.sh  $1 ;exit"
done 

3.3 shell操作

zookeeper中记录数据以Tree节点的形式存储数据的/类似于目录树!

目录叫znode : 节点的组织数据是 K V

基本操作指令

# 客户端连接 
bin/zkCli.sh 连接到本地zk服务
bin/zkCli.sh -server  linux02:2181  连接到执行节点的服务
quit  退出客户端      ctrl+c
-------------------------------------------
help 帮助命令  查看系统支持的所有的命令
ZooKeeper -server host:port cmd args
        stat path [watch]
        set path data [version]
        ls path [watch] * 
        delquota [-n|-b] path
        ls2 path [watch]  * 
        setAcl path acl
        setquota -n|-b val path
        history 
        redo cmdno
        printwatches on|off
        delete path [version]  * 
        sync path
        listquota path
        rmr path  * 
        get path [watch]
        create [-s] [-e] path data acl  * 
        addauth scheme auth
        quit   * 
        getAcl path
        close   * 
        connect host:port

-------------------------------------------------------------------
创建节点  节点必须以/开头  (路径必须是绝对路径)
--   create [-s] [-e] path data acl
create  /a  1 
create  /b  2 
create  /a/a1 11
create  /a/a2 22
create  /a/a3 33 
[-s] 在节点后面添加一个自增的id  防止节点名重复而创建失败
[-e]  创建的节点是临时节点  , 只在当前连接中有效
------节点分类-----
--临时节点
     临时有序   -e
     临时无序   -e  -s
--永久节点
     永久有序    -s
     永久无序   默认
注意: 节点的创建不能层级创建
查看节点列表 
ls    /a 
ls2   /a  显示数据版本 , 事务id 创建时间等信息

修改数据 
set  /a  123
获取数据 
get  /a 
删除节点  
delete   /a  只能删除空节点
rmr      /a  可以删除任意节点 

监听和通知

事件 : 触发了某种事件 通知

  • 节点数据的变化
  • 子节点个数的变化
  1. ls path [watch] 监控指定路径下数据的变化
  2. get path [watch] 监控指定路径数据的变化

注意: 监控和通知只能1次

打开两个zk客户端
1) 一个监控   2) 一个修改数据 

#  子节点个数变化
窗口01  :  ls  /   watch  
          ->(WatchedEvent state:SyncConnected type:NodeChildrenChangedpath:/)
窗口02  :  rmr  /a000000004 
---------------------------------------------------------------------------
#  节点数据变化
窗口01  :   get  /b   watch  
          ->(WatchedEvent state:SyncConnected type:NodeDataChanged path:/b)
窗口02  :  set /b   321

3.4 java操作

  • 添加依赖
  • 获取zk客户端
  • 调用API
  • 释放资源

添加依赖

<dependency>
  <groupId>org.apache.zookeeper</groupId>
  <artifactId>zookeeper</artifactId>
  <version>3.4.6</version>
</dependency>

入门示例-[获取连接\创建节点]

package com.doitedu.zk.cli;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
/**
 * @Date 2022/2/23
 * @Created by HANGGE
 * @Description TODO
 */
public class Demo01 {
    public static void main(String[] args) throws Exception {
        // 获取zk的客户端
        /**
         * 参数一 zk的地址  host:port,host2:port
         * 参数二 连接的超时时间  毫秒
         * 参数三 连接成功后的监听
         */
        ZooKeeper zk = new ZooKeeper("linux01:2181,linux02:2181,linux03:2181", 3000, null);
        // 调用API
        //1  创建节点
        /**
         * 参数1   路径
         * 参数2   数据 值 字节数组
         * 参数3   权限
         * 参数4   节点类型  1 2 3 4
         */
        zk.create("/doitedu" , "JINGYINGDOIT30_".getBytes() , ZooDefs.Ids.OPEN_ACL_UNSAFE , CreateMode.EPHEMERAL_SEQUENTIAL);
      //  System.out.println(zk);
        //释放资源
        Thread.sleep(8000);
        zk.close();

    }
}

zookeeper-Java-API使用

package com.doitedu.zk.cli;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import java.util.List;

/**
 * @Date 2022/2/23
 * @Created by HANGGE
 * @Description
 * 创建节点
 * 删除节点
 * 获取值
 * 修改值
 * 遍历子节点列表
 *   判断节点是否存在
 */
public class ClientApiDemo {
    public static void main(String[] args) throws Exception {
        // 获取客户端对象
        ZooKeeper zk = ZookeeperUtil.getZookeeper();
        // testGetData(zk);
        //修改数据
        //testSetData(zk);
        //获取子节点列表
        //testls(zk);
        // 只能删除空节点   递归
       // zk.delete("/b" , -1);
       // 递归删除节点
        ZookeeperUtil.rmr(zk,"/b") ;
        zk.close();
    }
    // 查看路径下的子节点列表
    public static void testls(ZooKeeper zk) throws KeeperException, InterruptedException {
        List<String> ls = zk.getChildren("/", null);
        if(ls!=null && ls.size()>0){
            for (String name : ls) {
                System.out.println("节点的名字是:" + name);
                byte[] data = zk.getData("/" + name, null, null);
                System.out.println(new String(data));
            }
        }
    }
    // 更新数据
    public static void testSetData(ZooKeeper zk) throws KeeperException, InterruptedException {
        Stat stat = zk.setData("/b", "liulan".getBytes(), -1);
        // 如果更新成功Stat 不为null
        if(stat != null){
            testGetData(zk);
        }
    }

    // 获取数据
    public static void testGetData(ZooKeeper zk) throws KeeperException, InterruptedException {

        byte[] data = zk.getData("/b", null, null);
        String str = new String(data);
        System.out.println(str);
    }
}

封装工具类

package com.doitedu.zk.cli;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.List;
/**
 * @Date 2022/2/23
 * @Created by HANGGE
 * @Description TODO
 */
public class ZookeeperUtil {
    /**
     * 获取指定zk集群的客户端对象
     *   linux01  02  03
     * @return
     * @throws IOException
     */
    public  static ZooKeeper  getZookeeper() throws IOException {
        return  new ZooKeeper("linux01:2181,linux02:2181,linux03:2181", 3000, null);
    }

    /**
     * 删除任意节点
     * @param zk
     * @param path
     * @throws KeeperException
     * @throws InterruptedException
     */
    public static void  rmr(ZooKeeper zk , String path) throws KeeperException, InterruptedException {
        // 遍历是否有子节点
        List<String> ls = zk.getChildren(path, null);
        // 有子节点
        if(ls!=null &&  ls.size() >0){
            // 删除子节点
            for (String name : ls) {
                //---递归
                rmr(zk , path+"/"+name);
            }
        }
        //没有子节点
        zk.delete(path , -1);
    }
}

监控和通知

  • 连接成功 监听通知
  • 数据变化
  • 节点变化

例子

package com.doitedu.zk.cli;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;

/**
 * @Date 2022/2/23
 * @Created by HANGGE
 * @Description 连接对象获取成功 事件通知
 */
public class TestWatcher01 {

    public static void main(String[] args) throws Exception {
        /**
         * 参数三  事件通知  监听器
         *  在获取对象的时候使用  当连接成功以后会  回调一次process方法
         */
        ZooKeeper zk = new ZooKeeper("linux11:2181", 2000, new Watcher() {
            public void process(WatchedEvent event) {
                System.out.println("获取连接成功......");
            }
        });
        System.out.println(zk);
        zk.close();

    }

}

----------------------------------------------------------------------
package com.doitedu.zk.cli;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

/**
 * @Date 2022/2/23
 * @Created by HANGGE
 * @Description
 * 监听节点数据的变化
 *   当监听的节点的数据发生变化    通知客户端  回调process
 */
public class TestWatcher02 {

    public static void main(String[] args) throws Exception {
        /**
         * 参数三  事件通知  监听器
         *  在获取对象的时候使用  当连接成功以后会  回调一次process方法
         */
        final ZooKeeper zk = new ZooKeeper("linux01:2181", 2000, null);
        // 获取指定节点数据的时候  绑定监听器  监听此节点数据的变化
        byte[] data = zk.getData("/b", new Watcher() {
            // /b节点数据变化就会执行这个方法   1次
            public void process(WatchedEvent event) {
                try {
                    System.out.println("/b的数据发生了变化....");
                    System.out.println(event.getType());
                    // 改变后的数据是
                    byte[] data1 = zk.getData("/b", this, null);
                    System.out.println("变化后的的数据是: "+new String(data1));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, null);
        System.out.println("变化以前的数据是: "+new String(data));

        Thread.sleep(Integer.MAX_VALUE);
        zk.close();
    }

}
-----------------------------------------------------------------------
package com.doitedu.zk.cli;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.util.List;

/**
 * @Date 2022/2/23
 * @Created by HANGGE
 * @Description
 * 监听节点数据的变化
 *   当监听的节点的数据发生变化    通知客户端  回调process
 */
public class TestWatcher03 {

    public static void main(String[] args) throws Exception {
        /**
         * 参数三  事件通知  监听器
         *  在获取对象的时候使用  当连接成功以后会  回调一次process方法
         */
        final ZooKeeper zk = new ZooKeeper("linux01:2181", 2000, null);

        List<String> ls = zk.getChildren("/", new Watcher() {
            // 当 / 节点的子节点 被删除  添加 执行这个方法
            public void process(WatchedEvent event) {
                try {
                    System.out.println(event.getType());
                    List<String> ls2 = zk.getChildren("/", this);
                    System.out.println("变化后的节点有:"+ls2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("当前的节点有: "+ls);

        Thread.sleep(Integer.MAX_VALUE);
        zk.close();
    }

}

4 原理基础

4.1 选举机制

在ZK中有三种状态 ;

Leader: 优先负责写数据

follower 存储数据 参与选举

observe 观察者 不参与选举 存储数据

每个服务器都有一个唯一的myid标记

初始启动选举

  1. ZK服务启动[linux01(myid=1)] , 发现集群中没有Leader .进入到选举状态 ; 投自己一票
  2. myid=1的机器获取1票 , 票数没有过半, 不能当选leader
  3. linux01, 广播投票结果
  4. linux02(myid=2)启动 ,发现集群中没有leader , 进入到选举状态
  5. 收到linux01的1票信息 , 发现linux02的myid > linux01的myid ;投自己一票 ,广播
  6. linux01收到广播 ,发现自己的myid小 ; 改投lnux02 1票 , 广播
  7. linux02 收到linux01的广播 ,自己的票数为2 , 过半 ,切换leader状态
  8. linux01 切换follower状态
  9. linux03启动 ,发现集群中有leader ;切换follower状态

运行过程中选举

注意: 要求整个集群中的数据是一致的! 有事务的保证! zxid事务id

1 先比较zxid的大小 , zxid大的优先当选

2 如果事务id一致 , 再比较myid

整个集群中有机器宕机 ,属于不正常的状态! 修复.........

数据一致性

zk中存储数据以KV的形式 ; K是以节点的组织的 以/开头

整个集群中所有的节点存储一样的数据

记录的数据一般为状态数据 ,配置数据 ,命令....数据量不大


本文转载自: https://blog.csdn.net/qq_37933018/article/details/123082841
版权归原作者 白眼黑刺猬 所有, 如有侵权,请联系我们删除。

“zookeeper随堂笔记”的评论:

还没有评论