10.zookeeper集群

zookeeper集群搭建

演示在单机环境下,基于一台虚拟机,进行zookeeper伪集群搭建,zookeeper集群中包含3个节点,节点对外提供服务端口号分别为2181、 2182、2183。

  1. 安装3份zookeeper(模拟3台服务器),安装目录分别为zookeeper2181、zookeeper2182、zookeeper2183,也可以基于原本已经安装有zookeeper的基础上直接复制再重命名。复制命令如下:zookeeper安装参考:2.zookeeper单机安装
cp -r zookeeper-3.5.3-beta zk_cluster/zookeeper2181
cp -r zookeeper-3.5.3-beta zk_cluster/zookeeper2182
cp -r zookeeper-3.5.3-beta zk_cluster/zookeeper2183
  1. 分别编辑3份{zookeeperHome}/conf/zoo.cfg配置文件(以zookeeper2181为例)
#服务器对应端口号,各自机器修改成自己相应的端口
clientPort=2181
#数据快照文件所在路径,各自机器修改为当前zk服务下的data目录,如果不存在该目录则先创建
dataDir=/opt/zk_cluster/zookeeper2181/data
#集群配置信息,该配置信息3台机器配置的都一样
#server.A=B:C:D 
#A:是一个数字,表示这个是服务器的编号 
#B:是这个服务器的ip地址 
#C:表示Flower跟Leader的通信端口,简称服务端内部通信的端口(默认2888)
#D:Leader选举的端口
server.1=192.168.64.132:2287:3387
server.2=192.168.64.132:2288:3388
server.3=192.168.64.132:2289:3389
  1. 在上一步配置文件dataDir 指定的目录下,创建 myid 文件,然后在该文件添加上一步server 配置的对应 A 数字。比如zookeeper2181对应的1、zookeeper2182对应的2,zookeeper2183对应的3。
echo "1" > /opt/zk_cluster/zookeeper2181/data/myid
echo "2" > /opt/zk_cluster/zookeeper2182/data/myid
echo "3" > /opt/zk_cluster/zookeeper2183/data/myid
  1. 分别启动3台服务器。
/opt/zk_cluster/zookeeper2181/bin/zkServer.sh start
/opt/zk_cluster/zookeeper2182/bin/zkServer.sh start
/opt/zk_cluster/zookeeper2183/bin/zkServer.sh start
  1. 查看3台服务器集群状态。
/opt/zk_cluster/zookeeper2181/bin/zkServer.sh status
/opt/zk_cluster/zookeeper2182/bin/zkServer.sh status
/opt/zk_cluster/zookeeper2183/bin/zkServer.sh status

在这里插入图片描述

一致性协议:zab协议

zab协议的全称是 Zookeeper Atomic Broadcast(zookeeper原子广播)。zookeeper 是通过 zab协议来保证分布式事务的最终一致性基于zab协议,zookeeper集群中的角色主要有以下三类,如下表所示:

角色描述
领导者(Leader)领导者负责投票的发起和决议,更新系统状态。
跟随着(Follower)用于接收客户端请求并向客户端返回结果,在选择过程中参与投票。
观察者(Observer)用于接收客户端连接请求,将写请求转发给leader节点,但observer不参与投票过程,只同步leader的状态。observer的目的是为了扩展系统,提高读取速度。通常对查询操作做负载。
客户端(Client)请求发起方

zab广播模式工作原理,通过类似两阶段提交协议的方式解决数据一致性:

在这里插入图片描述

  1. leader从客户端收到一个写请求。
  2. leader生成一个新的事务并为这个事务生成一个唯一的ZXID(事务ID)。
  3. leader将这个事务提议(propose,包含事务ID以及涉及写操作的一系列数据)发送给所有的follows节点。
  4. follower节点将收到的事务提议(propose)请求加入到历史队列(history queue)中,并发送ack给leader表示信息确认。
  5. 当leader收到大多数follower(半数以上节点)的ack消息,leader会发送提交事务(commit)请求。
  6. 当follower收到commit请求时,从历史队列中将事务请求commit。

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而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
  4. 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于server1、server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了leader。
  5. 改变服务器状态。一旦确定了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反馈。

配置如下:

  1. 在想要配置为observer角色机器上的zoo.cfg配置文件添加该配置:peerType=observer
  2. 在所有相关集群机器的zoo.cfg配置文件中,在对应服务编号后追加::observer,例如:server.3=192.168.64.132:2289:3389:observer
    在这里插入图片描述

zookeeper API连接集群

ZooKeeper构造函数

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher);
  • connectString:连接到zookeeper主机,如果是集群,多个ip则用,分隔。
  • sessionTimeout:会话超时时长,单位毫秒。
  • watcher:监视器对象。由于zookeeper API连接zookeeper服务是异步连接的,所以通过监控器来监听连接服务状态。
package com.huazai.zookeeper.zkexample.config;

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

import java.util.concurrent.CountDownLatch;

/**
 * @author pyh
 * @date 2021/5/19 0:20
 */
public class ZookeeperConnection {
    public static void main(String[] args) {
        connect();
    }

    public static ZooKeeper zooKeeper;

    public static ZooKeeper connect() {
        try {
            // 由于连接zookeeper服务器是异步连接,需要CountDownLatch阻塞主线程,等待子线程连接结果后反馈给主线程
            CountDownLatch countDownLatch = new CountDownLatch(1);
            /*
                connectString:服务器的ip和端口
                sessionTimeout:客户端与服务器之间的会话超时时间,以毫秒为单位的
                watcher:监视器对象
            */
            zooKeeper = new ZooKeeper("192.168.64.132:2181,192.168.64.132:2182,192.168.64.132:2184", 500000, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                        System.out.println("zookeeper异步连接成功");
                    } else if (watchedEvent.getState() == Event.KeeperState.Disconnected) {
                        System.out.println("断开连接");
                    } else if (watchedEvent.getState() == Event.KeeperState.Expired) {
                        System.out.println("会话超时");
                    } else if (watchedEvent.getState() == Event.KeeperState.AuthFailed) {
                        System.out.println("认证失败");
                    } else if (watchedEvent.getState() == Event.KeeperState.Closed) {
                        System.out.println("连接关闭");
                    }
                    countDownLatch.countDown();
                    System.out.println("使用构造函数默认的watcher");
                    System.out.println("path=" + watchedEvent.getPath());
                    System.out.println("eventType=" + watchedEvent.getType());
                }
            });
            // 主线程阻塞等待连接对象的创建成功
            countDownLatch.await();
            // 会话编号
            System.out.println("客户端sessionId:" + zooKeeper.getSessionId());
            return zooKeeper;
        } catch (Exception e) {
            System.out.println("zookeeper连接异常");
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭zookeeper会话
     */
    public static void close() {
        if (zooKeeper != null) {
            try {
                zooKeeper.close();
                System.out.println("zookeeper关闭成功");
            } catch (InterruptedException e) {
                System.out.println("zookeeper关闭失败");
                e.printStackTrace();
            }
        }
    }
}

测试结果如下:
在这里插入图片描述
java操作zookeeper参考:5.zookeeper javaAPI

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NPException.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值