• Zookeeper


    Zk介绍

    特点

    在这里插入图片描述

    数据结构

    ZooKeeper 数据模型的结构与 Unix 文件系统很类似,整体上可以看作是一棵树,每个
    节点称做一个 ZNode。每一个 ZNode 默认能够存储 1MB 的数据,每个 ZNode 都可以通过 其路径唯一标识
    在这里插入图片描述

    应用场景

    提供的服务包括:统一命名服务统一配置管理统一集群管理服务器节点动态上下线软负载均衡

    统一命名服务

    需要对应用 / 服务进行统一命名,便于识别。
    例如:IP不容易记住,而域名容易记住。
    在这里插入图片描述

    统一配置管理

    在这里插入图片描述

    统一集群管理

    在这里插入图片描述

    服务器动态上下线

    在这里插入图片描述

    软负载均衡

    Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去 处理最新的客户端请求

    在这里插入图片描述

    Zk安装、集群

    下载、启动

    https://zookeeper.apache.org/

    解压、配置zk

    tar -zxvf apache-zookeeper-3.7.1-bin.tar.gz  -C /opt/module/ # 解压
    cd /opt/module/  # 进入 该目录
    
    mv apache-zookeeper-3.7.1-bin/  zookeeper-3.7.1 # 改名
    cd /opt/module/zookeeper-3.7.1 # 进入 改名后的目录
    mkdir /opt/module/zookeeper-3.7.1/zkdata # 创建存放zk数据的目录
    cd conf/
    cp zoo_sample.cfg  zoo.cfg  # 拷贝 一份配置文件,重命名为 zoo.cfg
    vim zoo.cfg	 # 编辑配置文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    修改dataDir的目录

    在这里插入图片描述

    操作zk

    cd /opt/module/zookeeper-3.7.1/bin
    ./zkServer.sh start # 启动zk
    
    • 1
    • 2
    ./zkServer.sh status # 查看zk状态
    
    • 1

    在这里插入图片描述

    ./zkCli.sh # 连接zk服务端
    
    • 1

    在这里插入图片描述

    配置参数解读

    Zookeeper中的配置文件zoo.cfg中参数含义解读如下:
    1)tickTime = 2000:通信心跳时间,Zookeeper服务器与客户端心跳时间,单位毫秒
    在这里插入图片描述
    2)initLimit = 10:LF初始通信时限
    在这里插入图片描述

    Leader和Follower 初始连接时能 容忍的最多心跳数(tickTime的数量)
    3)syncLimit = 5:LF同步通信时限
    在这里插入图片描述

    Leader和Follower之间通信时间如果超过 syncLimit * tickTimeLeader认为Follwer死掉,从服务器列表中删除Follwer
    4)dataDir:保存Zookeeper中的数据
    注意:默认的tmp目录,容易被Linux系统定期删除,所以一般不用默认的tmp目录
    5)clientPort = 2181:客户端连接端口,通常不做修改

    Zookeeper 集群操作

    第一台机器

    解压、配置zk

    tar -zxvf apache-zookeeper-3.7.1-bin.tar.gz  -C /opt/module/ # 解压
    cd /opt/module/  # 进入 该目录
    
    mv apache-zookeeper-3.7.1-bin/  zookeeper-3.7.1 # 改名
    cd /opt/module/zookeeper-3.7.1 # 进入 改名后的目录
    mkdir /opt/module/zookeeper-3.7.1/zkdata # 创建存放zk数据的目录
    cd conf/
    cp zoo_sample.cfg  zoo.cfg  # 拷贝 一份配置文件,重命名为 zoo.cfg
    vim zoo.cfg	 # 编辑配置文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    修改dataDir的目录

    在这里插入图片描述

    cd /opt/module/zookeeper-3.7.1/zkdata/  # 进入数据目录
    vim  myid
    
    • 1
    • 2

    在文件中添加与 server 对应的编号注意:上下不要有空行,左右不要有空格
    在这里插入图片描述

    2
    
    • 1

    注意:添加 myid 文件,一定要在 Linux 里面创建,在 notepad++里面很可能乱码

    其他机器:

    其他不变,修改一下 myid 文件中的 值、修改 数据存储路径 配置

    在每台机器增加一下配置

    #######################cluster##########################
    server.2=hadoop102:2888:3888
    server.3=hadoop103:2888:3888
    server.4=hadoop104:2888:3888
    
    • 1
    • 2
    • 3
    • 4

    增加的 配置参数解读

    server.A=B:C:D
    
    • 1
    • A 是一个数字,表示这个是第几号服务器;
      集群模式下配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面有一个数据就是 A 的值

    Zookeeper 启动时读取此文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是哪个 server。

    • B 是这个服务器的地址;
    • C 是这个服务器 Follower 与集群中的 Leader 服务器交换信息的端口
    • D 是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口

    分别启动Zk即可

    选举机制(面试重点)

    第一次启动

    在这里插入图片描述
    在这里插入图片描述

    第一次启动

    启动选举规则:

    • ①EPOCH 大 的直接胜出
    • ②EPOCH 相同,事务 id 大的胜出
    • ③事务 id 相同,服务器 id 大的胜出

    生产集群安装多少 zk 合适?

    安装奇数台
    生产经验:
    ⚫ 10 台服务器:3 台 zk;
    ⚫ 20 台服务器:5 台 zk;
    ⚫ 100 台服务器:11 台 zk;
    ⚫ 200 台服务器:11 台 zk

    服务器台数多:好处,提高可靠性坏处:提高通信延时

    客户端命令行操作

    help  # 显示所有操作命令
    
    ls path  #使用 ls 命令来查看当前 znode 的子节点 [可监听]
    # -w 监听子节点变化
    # -s 附加次级信息
    
    create # 普通创建
    #-s 含有序列
    #-e 临时(重启或者超时消失)
    
    get path # 获得节点的值 [可监听]
    # -w  监听节点内容变化
    # -s  附加次级信息
    
    set  # 设置节点的具体值
    
    stat # 查看节点状态
    
    delete # 删除节点
    
    deleteall # 递归删除节点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    客户端界面

    可以下载 prettyZoo-win.msi 进行操作,更直观

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    节点类型(持久 / 短暂 / 有序号 / 无序号)

    在这里插入图片描述
    1)分别创建 2个 普通节点(永久节点 + 不带序号)

    create /sanguo "liubei"
    
    • 1

    注意:创建节点时,要赋值

    2)获得节点的值

    get /sanguo 
    
    • 1

    在这里插入图片描述

    get -s /sanguo 
    
    • 1

    在这里插入图片描述
    3)创建带序号的节点(永久节点 + 带序号)

    先创建一个普通的根节点 /sanguo/weiguo,默认是 持久化,不带序号 的节点

    create /sanguo/weiguo "caocao" 
    
    • 1

    创建 带序号的节点

    create -s /sanguo/weiguo "caocao"
    
    • 1

    在这里插入图片描述
    如果原来没有序号节点,序号从 0 开始依次递增。如果原节点下已有 2 个节点,则再排序时从 2 开始,以此类推

    4)创建短暂节点( 短暂节点 + 不带序号 or 带序号)
    创建 短暂的 不带序号的 节点

    create -e /sanguo/weiguo "x1"
    
    • 1

    创建短暂的带序号的节点

    create -es /sanguo/weiguo "x2"
    
    • 1

    5)修改 节点数据值

    set /sanguo/weiguo0000000006 xiaowang 
    
    • 1

    在这里插入图片描述

    监听器

    1)节点的值变化监听

    注册监听 /sanguo 节点数据变化

    get -w /sanguo
    
    • 1

    修改 /sanguo 节点的数据
    在这里插入图片描述
    type:NodeDataChanged 代表 节点数据变化
    在这里插入图片描述

    2)节点的子节点变化监听(路径变化)

    监听/sanguo 节点的子节点变化

    ls -w /sanguo
    
    • 1

    在这里插入图片描述

    在这里插入图片描述
    type:NodeChildrenChanged 子节点变化
    在这里插入图片描述

    节点删除与查看

    # 创建节点
    create /sanguo/jin
    # 删除节点
    delete /sanguo/jin  
    # 递归 删除节点
    deleteall /sanguo/shuguo
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查看 节点状态

    stat /sanguo 
    
    • 1

    在这里插入图片描述

    客户端 API 操作

    <dependency>
        <groupId>org.apache.zookeepergroupId>
        <artifactId>zookeeperartifactId>
        <version>3.7.1version>
    dependency>
    
      <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <scope>testscope>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    创建 ZooKeeper 客户端

    import lombok.SneakyThrows;
    import org.apache.zookeeper.*;
    import org.apache.zookeeper.data.Stat;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import java.util.List;
    
    @RunWith(SpringRunner.class)
    public class DemoApplicationTests {
    
    
        // 注意:多个 zk服务器 用逗号分隔
        //      逗号前后 不能有空格
        private static String connectString =
                "192.168.111.101:2181";
        private static int sessionTimeout = 2000;
        private ZooKeeper zkClient;
    
        @Before
        public void init() throws Exception {
            zkClient = new ZooKeeper (connectString, sessionTimeout, new Watcher () {
                @SneakyThrows
                @Override
                public void process(WatchedEvent watchedEvent) {
                    //  收到 事件通知后的 回调函数(用户的业务逻辑)
                    System.out.println (watchedEvent.getType () + "--"
                            + watchedEvent.getPath ());
                    // 再次启动监听
                    List<String> children = zkClient.getChildren ("/", true);
                    for (String child : children) {
                        System.out.println (child);
                    }
    
                }
            });
        }
    
        // 创建子节点
        @Test
        public void create() throws Exception {
            /**
             * 参数 1:要创建的节点的路径;
             * 参数 2:节点数据 ;
             * 参数 3:节点权限 ;
             * 参数 4:节点的类型
             *
             */
            String nodeCreated = zkClient.create ("/xiag",
                    "shuaige".getBytes (), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.PERSISTENT);
    
            Thread.sleep (Long.MAX_VALUE);
        }
    
        // 获取子节点并监听节点变化
        @Test
        public void getChildren() throws Exception {
            List<String> children = zkClient.getChildren ("/", true);
            for (String child : children) {
                System.out.println (child);
            }
            // 延时阻塞
            Thread.sleep (Long.MAX_VALUE);
        }
    
        // 判断 znode 是否存在
        @Test
        public void exist() throws Exception {
            Stat stat = zkClient.exists ("/atguigu", false);
            System.out.println (stat == null ? "not exist" : "exist");
        }
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    案例

    服务器动态上下线

    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.ZooKeeper;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class DistributeClient {
        private static String connectString =
                "192.168.111.101:2181";
        private static int sessionTimeout = 2000;
        private ZooKeeper zk = new ZooKeeper (connectString, sessionTimeout, new Watcher () {
            @Override
            public void process(WatchedEvent event) {
    
            }
        });
        private String parentNode = "/servers";
    
        public DistributeClient() throws IOException {
        }
    
    
        // 获取服务器列表信息
        // 获取 servers 的子节点信息,从中获取服务器信息列表
        public void getServerList() throws Exception {
            // 1 获取服务器子节点信息
            // 第一次 调用该方法时 注册监听事件、添加 回调函数
            // 然后 获取节点数据 运行下面的逻辑
            List<String> children = zk.getChildren (parentNode, event -> {
                // 监听的事件 发生时, 第一步 注册监听事件、添加 回调函数
                // 然后 再执行下面的业务!!必须 要每次都要注册!形成循环~
                try {
                    getServerList ();
                } catch (Exception e) {
                    throw new RuntimeException (e);
                }
            });
            // 2 存储服务器信息列表
            ArrayList<String> servers = new ArrayList<> ();
            // 3 遍历 所有节点,获取 节点中的主机名称信息
            for (String child : children) {
                byte[] data = zk.getData (parentNode + "/" + child,
                        false, null);
                if (data == null || data.length == 0) continue;
                servers.add (new String (data));
            }
    
            // 4 打印服务器列表信息
            System.out.println (servers);
    
        }
    
        // 业务功能
        public void business() throws Exception {
            System.out.println ("client is working ...");
            Thread.sleep (Long.MAX_VALUE);
        }
    
        public static void main(String[] args) throws Exception {
            // 1 获取 zk 连接
            DistributeClient client = new DistributeClient ();
            //注册监听事件
            client.getServerList ();
            // 2 业务进程启动
            client.business ();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    # 创建节点 /servers
    create  /servers
    
    • 1
    • 2

    在这里插入图片描述
    在这里插入图片描述
    删除一个节点
    在这里插入图片描述

    动态监听是否有流量

    
    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.ZooKeeper;
    
    import java.io.IOException;
    
    public class DistributeClient {
        private static String connectString =
                "192.168.111.101:2181";
        private static int sessionTimeout = 2000;
        private ZooKeeper zk = new ZooKeeper (connectString, sessionTimeout, new Watcher () {
            @Override
            public void process(WatchedEvent event) {
    
            }
        });
        ;
    
        public DistributeClient() throws IOException {
        }
    
    
        public void hasFlow() throws Exception {
            // 第一次 调用该方法时 注册监听事件、添加 回调函数
            // 然后 获取节点数据 运行下面的逻辑
            byte[] data = zk.getData ("/hasFlow", event -> {
                // 监听的事件 发生时, 第一步 注册监听事件、添加 回调函数
                // 然后 再执行下面的业务!!必须 要每次都要注册!形成循环~
                try {
                    hasFlow ();
                } catch (Exception e) {
                    throw new RuntimeException (e);
                }
            }, null);
    
            String dataStr = new String (data);
            if ("false".equals (dataStr))
                System.out.println ("No Flow!");
            else if ("true".equals (dataStr))
                System.out.println ("Flow  is ok ");
    
        }
    
        // 业务功能
        public void business() throws Exception {
            System.out.println ("client is working ...");
            Thread.sleep (Long.MAX_VALUE);
        }
    
        public static void main(String[] args) throws Exception {
            // 1 获取 zk 连接
            DistributeClient client = new DistributeClient ();
            //注册监听事件
            client.hasFlow ();
            client.business ();
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    # 先创建节点 /hasFlow 
    create  /hasFlow  ""
    
    • 1
    • 2

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    [附源码]Python计算机毕业设计出版社样书申请管理系统
    脑机接口的商业化道路,还要走多远多长?
    【PostgreSQL15-beta1版本系统表、系统视图、等待事件变化】
    python版 html正文提取(CEPF)
    深入了解Jedis:Java操作Redis的常见类型数据存储
    resubmit 渐进式防重复提交框架简介
    照片加水印怎么弄?方法步骤介绍
    解决LiveData数据倒灌的新思路
    分享 2022 年最受欢迎的黑科技工具(二)
    Visual Studio里的编译、生成与调试
  • 原文地址:https://blog.csdn.net/qq_30659573/article/details/128028768