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	 # 编辑配置文件

修改dataDir的目录

在这里插入图片描述

操作zk

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

在这里插入图片描述

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

在这里插入图片描述

配置参数解读

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	 # 编辑配置文件

修改dataDir的目录

在这里插入图片描述

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

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

2

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

其他机器:

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

在每台机器增加一下配置

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

增加的 配置参数解读

server.A=B:C:D
  • 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 # 递归删除节点

客户端界面

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

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

create /sanguo "liubei"

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

2)获得节点的值

get /sanguo 

在这里插入图片描述

get -s /sanguo 

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

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

create /sanguo/weiguo "caocao" 

创建 带序号的节点

create -s /sanguo/weiguo "caocao"

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

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

create -e /sanguo/weiguo "x1"

创建短暂的带序号的节点

create -es /sanguo/weiguo "x2"

5)修改 节点数据值

set /sanguo/weiguo0000000006 xiaowang 

在这里插入图片描述

监听器

1)节点的值变化监听

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

get -w /sanguo

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

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

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

ls -w /sanguo

在这里插入图片描述

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

节点删除与查看

# 创建节点
create /sanguo/jin
# 删除节点
delete /sanguo/jin  
# 递归 删除节点
deleteall /sanguo/shuguo

查看 节点状态

stat /sanguo 

在这里插入图片描述

客户端 API 操作

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

  <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

创建 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");
    }
}



案例

服务器动态上下线

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 ();
    }
}

# 创建节点 /servers
create  /servers

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

动态监听是否有流量


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 ();
    }
}


# 先创建节点 /hasFlow 
create  /hasFlow  ""

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值