zookeeper原理与实践

基本介绍

Zookeeper可以看成是一个拥有文件系统特点的分布式数据库,为分布式系统提供一致性协调服务。可以用来作为统一命名服务,集群管理,分布式配置管理组件来使用。

通常来说,Zookeeper以集群形式部署,包含多个server节点, 其中一个leader,多个follower。

数据模型

Zookeeper的数据模型结构与unix文件系统类似,整体上可以看作是一棵树,每个节点是一个ZNode。每个ZNode可以通过其路径唯一标识。

与文件系统不同的是,zookeeper的每个节点既可以当成目录,也可以看成文件,因为节点上可以存储少量的内容。节点数据默认最大值为1M,可以通过配置修改,但通常不建议在ZNode上存储大量数据。

Zookeeper的文件结构如下图所示:
在这里插入图片描述
ZNode根据本身的特性可以分为四类:

  • 持久化节点
    客户端与zookeeper断开连接后,该节点依旧存在。
  • 持久化顺序节点
    客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号。
  • 临时节点
    客户端与zookeeper断开连接后,该节点被删除。
  • 临时顺序节点
    客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

通知机制

客户端注册监听它关心的节点,当节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

zookeeper安装和使用

从清华镜像站下载zookeeper3.6.3版本,地址:https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.3.
在这里插入图片描述

下载到本地并解压缩后(注意下载3.6.3-bin.tar.gz),进入zk目录修改配置文件名:
cp conf/zoo_sample.cfg conf/zoo.cfg
接着进入bin目录启动服务端:
./zkServer.sh start
服务端启动后,继续启动客户端:
./zkCli.sh
就进入客户端命令行了。

简单的创建节点,查看节点,设置节点数据的操作如下:
在这里插入图片描述

Java访问zookeeper

先创建一个节点:
在这里插入图片描述
在代码中引入maven依赖包:

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

创建一个简单demo来监听zk节点数据的变更:

public class ZookeeperProSync implements Watcher {

    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
    private static ZooKeeper zk = null;
    private static Stat stat = new Stat();

    public static void main(String[] args) throws Exception {
        //zk path
        String path = "/username";
        // connect to zk and register a listener
        zk = new ZooKeeper("127.0.0.1:2181", 5000, new ZookeeperProSync());
        // wait for zk connected
        connectedSemaphore.await();
        System.out.println(new String(zk.getData(path, true, stat)));
        Thread.sleep(Integer.MAX_VALUE);
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        if (Event.KeeperState.SyncConnected == watchedEvent.getState()) {
            if (Event.EventType.None == watchedEvent.getType() && null == watchedEvent.getPath()) {
                connectedSemaphore.countDown();
            } else if (watchedEvent.getType() == Event.EventType.NodeDataChanged) {
                try {
                    System.out.println("config has changed, new value is " + new String(zk.getData(watchedEvent.getPath(), true, stat)));
                } catch (Exception e) {

                }
            }
        }
    }
}

原始的节点配置数据为howe,通过set指令将其修改为howee。输出结果如下:
在这里插入图片描述

集群部署

在一台机器上模拟集群环境来安装,复制zoo.cfg文件为zoo1.cfg,zoo2.cfg,zoo3.cfg。
修改zoo1.cfg文件, 主要是数据路径和节点配置:

# vim conf/zoo-1.cfg
dataDir=/tmp/zookeeper-1
clientPort=2181
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

接着修改zoo2和zoo3,但只需要修改dataDir和clientPort,数据路径分别为zookeeper-2,zookeeper-3,端口分别为2082和2083。

其他配置项说明:

  • tickTime:Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔。
  • initLimit:Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是zk服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10*2000=20 秒。
  • syncLimit:Leader 与 Follower 之间进行数据同步时,请求和应答的时间长度,单位是tickTime,总的时间长度就是 5*2000=10秒。
  • dataDir:Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
  • clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
  • server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是当集群中的 Leader 服务器宕机时,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。本文这里是伪集群的配置方式,B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,要给它们分配不同的端口号。

当启动一个服务器时,我们需要知道启动的是哪个服务器。一个服务器通过读取data目录下一个名为myid的文件来获取服务器ID信息。通过以下命令来创建这些文件创建三个文件夹/tmp/zookeeper-1,/tmp/zookeeper-2,/tmp/zookeeper-2,也就是上面设置的dataDir目录。在每个目录中创建文件myid 文件,写入当前实例的server id:

# cd /tmp/zookeeper-1
# vim myid
1
# cd /tmp/zookeeper-2
# vim myid
2
# cd /tmp/zookeeper-3
# vim myid
3

启动三个实例:

# bin/zkServer.sh start conf/zoo1.cfg
# bin/zkServer.sh start conf/zoo2.cfg
# bin/zkServer.sh start conf/zoo3.cfg

检测集群状态:

第一次启动的时候最开始始终有一个节点是standalone单机模式,后来全部退出重新启动之后就正常了。

原子广播协议

Zookeeper的核心是原子广播,通过原子广播协议(ZAB)保证各个server之间的数据同步。Zab协议有两种模式,分别是恢复模式和广播模式。

当服务启动或者在leader崩溃后,zab进入恢复模式。在恢复模式中,每个节点都发起投票选举leader。当超过半数节点投票同一个节点,那这个节点就选作为了leader。当leader被选举出来,并且大多数节点完成了和leader的状态同步之后,恢复模式就结束了。随后就进入广播模式,接受客户端的请求,并作为事务来处理。

恢复模式

当zookeeper的server节点启动时,先给自己投票,选票里包含server id和zxid(事务id)。当两个以上节点启动后,节点之间还会彼此交换投票信息,并比较当前的投票,先比较事务id,再比较server id,取id较大的投票为leader投票。
举个例子说明,在上文介绍zk集群部署时,三个server节点的启动顺序分别为zoo1,zoo2,zoo3,则zoo1和zoo2分别先投给自己,然后交换彼此的投票,比较投票信息里的server id和zxid,刚启动时zxid相同,zoo2的server id为2,比zoo1的大,因此zoo1会更改投票,将自己的票投给zoo2。这样zoo2就获得了两票,超过总节点数的一半,因此zoo2将成为leader,zoo1成为follower,如下图所示:
在这里插入图片描述
再继续启动zoo3,由于集群中已经有了leader节点,此时zoo3就以follower角色加入集群中。

如果以zoo2,zoo3,zoo1的顺序启动,可以推测出zoo3将作为leader节点。实际也是如此:
在这里插入图片描述

广播模式

Zookeeper集群进入广播模式后,就可以接受客户端请求,并进行事务处理。
当然并不是所有的请求都是事务性请求,比如读请求,不涉及数据的修改,直接从接受请求的节点返回要读取的数据即可。
真正的事务性请求是写请求。客户端发出一个写请求之后,接受请求的节点会将这个请求转发给leader节点,leader节点再通过两阶段提交的方式来提交事务:首先通知所有的follower进行事务预提交,当超过一半的follower返回确认之后,才真正的提交事务。

参考资料

[1]. https://blog.csdn.net/java_66666/article/details/81015302

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值