Zookeeper

Zookeeper的介绍和安装

Zookeeper概述

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。   Zookeeper是一个分布式协调服务;就是为用户的分布式应用程序提供协调服务

序号功能
1为别的分布式程序服务的
2本身就是一个分布式程序
3主从协调 服务器节点动态上下线 统一配置管理 分布式共享锁 统一名称服务
4管理(存储,读取)用户程序提交的数据 并为用户程序提供数据节点监听服务

Zookeeper的集群机制

 Zookeeper是为其他分布式程序提供服务的,所以本身自己不能随便就挂了,所以zookeeper自身的集群机制就很重要。zookeeper的集群机制采用的是半数存活机制,也就是整个集群节点中有半数以上的节点存活,那么整个集群环境可用。这也就是说们的集群节点最好是奇数个节点。

Zookeeper集群节点的角色

Leader

Leader服务器是Zookeeper集群工作的核心,其主要工作如下

  1. 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。

  2. 集群内部各服务器的调度者

Follower

  Follower是Zookeeper集群的跟随者,其主要工作如下

  1. 处理客户端非事务性请求(读取数据),转发事务请求给Leader服务器。

  2. 参与事务请求Proposal的投票。

  3. 参与Leader选举投票。

Observer   Observer充当观察者角色,观察Zookeeper集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给Leader服务器进行处理。Observer不会参与任何形式的投票,包括事务请求Proposal的投票和Leader选举投票

集群环境准备

准备3个节点

通过VMware的克隆或者直接复制文件夹的方式来创建另外两个新的节点

配置好jdk1.8环境

修改主机ip(确保三台节点的ip不重复即可),并且删除掉uuid

 vim /etc/sysconfig/network-scripts/ifcfg-ens33

重启网络服务

service network restart

ping测试,检查网络是否正常

节点的映射关系

每个节点设置相应的ip和主机名的映射关系,方便集群环境的部署

修改hosts配置文件中的信息

三台节点/etc/hosts中需要增加的内容

192.168.92.101 node1
192.168.92.102 node2
192.168.92.103 node3

配置免密登录

方便后续节点之间的远程拷贝文件时无需输入密码

生成公钥和私钥

输入命令后根据提示,三或四次回车即可

发送公钥给需要免密登录的节点

ssh-copy-id node1

ssh-copy-id node2

ssh-copy-id node3

关闭防火墙

  1. 查看防火墙状态:

    sudo systemctl status firewalld
    

    这个命令将显示防火墙服务 firewalld 的状态,包括是否正在运行。

如果你想永久禁用防火墙,可以按照以下步骤进行操作:

  1. 停止防火墙服务:

    sudo systemctl stop firewalld
    

    这个命令将停止 firewalld 服务,即时生效。

  2. 禁用防火墙服务的自启动:

    sudo systemctl disable firewalld
    

    这个命令将防止 firewalld 服务在系统启动时自动启动。

  3. 检查防火墙状态,以确保防火墙已停止:

    sudo systemctl status firewalld
    

    它将显示防火墙服务的状态,应该显示为停止状态。

Zookeeper集群环境搭建

获取安装文件

下载地址:Index of /apache/zookeeper

通过wget命令将安装文件下载到opt目录下

注意:

apache-zookeeper-3.7.2-bin.tar.gz和apache-zookeeper-3.7.2.tar.gz的区别 -bin是编译后的文件 我们用这个

wget https://mirrors.bfsu.edu.cn/apache/zookeeper/zookeeper-3.7.2/apache-zookeeper-3.7.2-bin.tar.gz

如果提示证书过期,使用 --no-check-certificate 参数来跳过证书检查

wget --no-check-certificate https://mirrors.bfsu.edu.cn/apache/zookeeper/zookeeper-3.7.2/apache-zookeeper-3.7.2-bin.tar.gz

解压缩文件

tar -zxf apache-zookeeper-3.7.2-bin.tar.gz

修改配置

修改zoo.cfg文件,系统默认的名称是 zoo_smple.cfg我们需要重命名为zoo.cfg

修改配置内容

配置myid文件

我们需要在Zookeeper的数据存储的目录中创建一个myid文件,文件中的内容只有一行信息,即表示我们集群几点的标识,范围是1-255,每个节点的myid的数字和我们在zoo.cfg中配置的server.数字是对应的

/opt//myid

1

分发文件(包含myid、zookeeper)

scp -r zookeeper/ node2:`pwd`

scp -r zookeeper/ node3:`pwd`

scp -r apache-zookeeper-3.7.2-bin/ node2:`pwd`

scp -r apache-zookeeper-3.7.2-bin/ node3:`pwd`

记得覆盖其他节点的myid文件,写入各自的myid

echo 2 > /opt/zookeeper/data/myid

echo 3 > /opt/zookeeper/data/myid

启动测试

启动

./bin/zkServer.sh start

查看节点状态

./bin/zkServer.sh status

Zookeeper的选举机制

为什么要进行Leader选举? Leader 主要作用是保证分布式数据一致性,即每个节点的存储的数据同步。遇到以下两种情况需要进行Leader选举

  • 服务器初始化启动

  • 服务器运行期间无法和Leader保持连接,Leader节点崩溃,逻辑时钟崩溃。

服务器初始化时Leader选举

Zookeeper由于其自身的性质,一般建议选取奇数个节点进行搭建分布式服务器集群。以3个节点组成的服务器集群为例,说明服务器初始化时的选举过程。启动第一台安装Zookeeper的节点时,无法单独进行选举,启动第二台时,两节点之间进行通信,开始选举Leader。

  1. 每个Server投出一票。他们两都选自己为Leader,投票的内容为(SID,ZXID)。 SID即Server的id,安装zookeeper时配置文件中所配置的myid;ZXID,事务id, 为节点的更新程度,ZXID越大,代表Server对Znode的操作越新。由于服务器初始化, 每个Sever上的Znode为0,所以Server1投的票为(1,0),Server2为(2,0)。 两Server将各自投票发给集群中其他机器。

  2. 每个Server接收来自其他Server的投票。集群中的每个Server先判断投票有效性, 如检查是不是本轮的投票,是不是来Looking状态的服务器投的票。

  3. 对投票结果进行处理。先了解下处理规则

  • 首先对比ZXID。ZXID大的服务器优先作为Leader

  • 若ZXID相同,比如初始化的时候,每个Server的ZXID都为0,

  • 就会比较myid,myid大的选出来做Leader。

    对于Server而言,他接受到的投票为(2,0),因为自身的票为(1,0),所以此时它会选举Server2为Leader, 将自己的更新为(2,0)。而Server2收到的投票为Server1的(1,0)由于比他自己小, Server2的投票不变。Server1和Server2再次将票投出,投出的票都为(2,0)。

  1. 统计投票。每次投票之后,服务器都会统计投票信息,如果判定某个Server有过半的票数投它, 那么该Server将会作为Leader。对于Server1和Server2而言,统计出已经有两台机器接收了(2,0)的投票信息, 此时认为选出了Leader。

  2. 改变服务器状态。当确定了Leader之后,每个Server更新自己的状态, Leader将状态更新为Leading,Follower将状态更新为Following。

服务器运行期间的Leader选举

  Zookeeper运行期间,如果有新的Server加入,或者非Leader的Server宕机,那么Leader将会同步数据到新Server或者寻找其他备用Server替代宕机的Server。若Leader宕机,此时集群暂停对外服务,开始在内部选举新的Leader。假设当前集群中有Server1、Server2、Server3三台服务器,Server2为当前集群的Leader,由于意外情况,Server2宕机了,便开始进入选举状态。过程如下

  1. 变更状态。其他的非Observer服务器将自己的状态改变为Looking,开始进入Leader选举。

  2. 每个Server发出一个投票(myid,ZXID),由于此集群已经运行过,所以每个Server上的ZXID可能不同。 假设Server1的ZXID为145,Server3的为122,第一轮投票中,Server1和Server3都投自己, 票分别为(1,145)、(3,122),将自己的票发送给集群中所有机器。

  3. 每个Server接收接收来自其他Server的投票,接下来的步骤与初始化时相同。

Zookeeper客户端使用

配置Zookeeper的环境变量

为了简化我们每次操作Zookeeper而不用进入到Zookeeper的安装目录,我们可以将Zookeeper的安装信息配置到系统的环境变量中

vim /etc/profile

添加的内容

export ZOOKEPPER_HOME=/opt/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin

执行source命令

source /etc/profile

我们就可以在节点的任意位置操作Zookeeper了

通过scp命令将profile文件发送到其他几个节点上

scp /etc/profile node2:/etc/profile

scp /etc/profile node3:/etc/profile

任意位置连接客户端

zkCli.sh sh


数据操作

Zookeeper的数据结构

  1. 层次化的目录结构,命名符合常规文件系统规范

  2. 每个节点在Zookeeper中叫做znode,并且有一个唯一的路径标识

  3. 节点znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点)

  4. 客户端应用可以在节点上设置监听器

节点类型

znode有两种类型

短暂性(ephemeral,断开连接自己删除) 持久性(persistent,断开连接不删除)

znode有四种形式的目录节点(默认是persistent)如下

序号节点类型描述
1PERSISTENT持久节点
2PERSISTENT_SEQUENTIAL持久有序节点
3EPHEMERAL短暂节点
4EPHEMERAL_SEQUENTIAL短暂有序节点

常用命令

        Zookeeper作为Dubbo的注册中心用来保存我们各个服务的节点信息,显示Zookeeper是可以实现输出的存储操作的,我们来看下Zookeeper中存储操作的基本命令

ls

ls用来查看某个节点下的子节点信息

增强的命令,查看节点下的子节点及当前节点的属性信息 ls2或者 ls -s 命令

create

创建节点信息

get

get命令用来查看节点的数据

如果要查看节点的属性信息那么我们可以通过get -s 来实现

delete

delete只能删除没有子节点的节点要删除非空节点可以通过 rmr 或者 deleteall 命令实现

set

set命令可以用来修改节点的内容

事件监听

数据改变的监听

监听某个节点的数据内容变化,通过get命令 带 -w 参数即可,在3.4版本的Zookeeper中是通过get path watch 来说实现监控的

注意监听一次节点只会触发一次,如果要实现多次监听,那么可以在触发事件的处理函数中再次追加对节点的监听操作

子节点的改变

监听节点下面的子节点的改变。

ls -w /app1

Zookeeper Java API使用

可以在zookeeper的lib目录下看到对应的依赖

注意这里一定要zookeeper的maven依赖和zookeeper版本要保持一致,否则会导致不能正常使用。

引入maven依赖

<dependencies>
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.7.2</version>
    </dependency>
    <dependency>
        <groupId>com.github.sgroschupf</groupId>
        <artifactId>zkclient</artifactId>
        <version>0.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>

</dependencies>

连接ZK服务

public void test1() throws IOException {
        zooKeeper = new ZooKeeper(connectString, sessionTimeOut, new Watcher() {
            /**
             * 触发监听事件的回调方法
             * @param watchedEvent
             */
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("触发了.....");
            }
        });
        System.out.println("--->" + zooKeeper);
    }

基本操作

public class Test1 {

    private String connectString = "192.168.92.101:2181,192.168.92.102:2181,192.168.92.103:2181";

    private int sessionTimeOut = 10000;

    ZooKeeper zooKeeper = null;

    /**
     * 连接Zookeeper服务端
     */
    @Before
    public void test1() throws IOException {
        zooKeeper = new ZooKeeper(connectString, sessionTimeOut, new Watcher() {
            /**
             * 触发监听事件的回调方法
             * @param watchedEvent
             */
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("触发了.....");
            }
        });
        System.out.println("--->" + zooKeeper);
    }

    /**
     * 创建节点
     */
    @Test
    public void createNode() throws Exception{
        String path = zooKeeper.create("/apptest" // 节点路径
                ,"HelloZookeeper".getBytes() // 节点的数据
                , ZooDefs.Ids.OPEN_ACL_UNSAFE // 权限
        , CreateMode.PERSISTENT // 节点类型
        );
        System.out.println(path);
    }

    /**
     * 判断节点是否存在
     */
    @Test
    public void exist() throws  Exception{
        // true表示的是使用Zookeeper中的watch
        Stat stat = zooKeeper.exists("/apptest", true);
        if(stat != null){
            System.out.println("节点存在"+ stat.getNumChildren());
        }else{
            System.out.println("节点不存在 ....");
        }
    }

    /**
     * 获取某个节点下面的所有的子节点
     */
    @Test
    public void getChildrens() throws Exception{
        List<String> childrens = zooKeeper.getChildren("/app1", true);
        for (String children : childrens) {
            // System.out.println(children);
            // 获取子节点中的数据
            byte[] data = zooKeeper.getData("/app1/" + children, false, null);
            System.out.println(children+":" + new String(data));
        }
    }

    /**
     * 修改节点的内容
     */
    @Test
    public void setData() throws Exception{
        // -1 不指定版本 自动维护
        Stat stat = zooKeeper.setData("/app1/a1", "666666".getBytes(), -1);
        System.out.println(stat);
    }


    /**
     * 删除节点
     */
    @Test
    public void deleteNode() throws Exception{
        zooKeeper.delete("/app1",-1);

    }


    /**
     * 监听Node节点下的子节点的变化
     */
    @Test
    public void nodeChildrenChange() throws Exception{
        List<String> list = zooKeeper.getChildren("/app1", new Watcher() {

            /**
             *              None(-1),
             *             NodeCreated(1),
             *             NodeDeleted(2),
             *             NodeDataChanged(3),
             *             NodeChildrenChanged(4),
             *             DataWatchRemoved(5),
             *             ChildWatchRemoved(6);
             * @param watchedEvent
             */
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("--->"+ watchedEvent.getType());
            }
        });
        for (String s : list) {
            System.out.println(s);
        }

        Thread.sleep(Integer.MAX_VALUE);
    }

    /**
     * 监听节点内容变更
     */
    @Test
    public void nodeDataChanged() throws Exception{
        byte[] data = zooKeeper.getData("/app1/a1", new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("--->" + watchedEvent.getType());
            }
        }, null);
        System.out.println("--->" + new String(data));
        Thread.sleep(Integer.MAX_VALUE);
    }
}

事件监听处理

Java程序监听Zookeeper中的数据的变化

/**
     * 监听Node节点下的子节点的变化
     */
    @Test
    public void nodeChildrenChange() throws Exception{
        List<String> list = zooKeeper.getChildren("/app1", new Watcher() {

            /**
             *              None(-1),
             *             NodeCreated(1),
             *             NodeDeleted(2),
             *             NodeDataChanged(3),
             *             NodeChildrenChanged(4),
             *             DataWatchRemoved(5),
             *             ChildWatchRemoved(6);
             * @param watchedEvent
             */
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("--->"+ watchedEvent.getType());
            }
        });
        for (String s : list) {
            System.out.println(s);
        }

        Thread.sleep(Integer.MAX_VALUE);
    }

    /**
     * 监听节点内容变更
     */
    @Test
    public void nodeDataChanged() throws Exception{
        byte[] data = zooKeeper.getData("/app1/a1", new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("--->" + watchedEvent.getType());
            }
        }, null);
        System.out.println("--->" + new String(data));
        Thread.sleep(Integer.MAX_VALUE);
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值