WinddSnow

Java面试题13Zookeeper

字数统计: 4.2k阅读时长: 15 min
2022/10/22

Zookeeper 是什么

Zookeeper 是一个分布式协调服务的开源框架, 主要用来解决分布式集群中应用系统的一致性问题, 例如怎样避免同时操作同一数据造成脏读的问题.

ZooKeeper 本质上是一个分布式的小文件存储系统. 提供基于类似于文件系统的目录树方式的数据存储, 并且可以对树中的节点进行有效管理. 从而用来维护和监控你存储的数据的状态变化. 通过监控这些数据状态的变化,从而可以达到基于数据的集群管理.

在大数据生态系统里,很多组件的命名都是某种动物,比如 hadoop 就是大象, hive就是蜜蜂, 而 Zookeeper 就是动物管理员.

Zookeeper 的数据模型

  • ZK 本质上是一个分布式的小文件存储系统.
  • ZK 表现为一个分层的文件系统目录树结构, 既能存储数据, 而且还能像目录一样有子节点. 每个节点可以存最多 1M 左右的数据.
  • 每个节点称做一个 Znode, 每个 Znode 都可以通过其路径唯一标识.
  • 而且客户端还能给节点添加 watch, 也就是监听器, 可以监听节点的变化, 这个功能常在实际开发中作为监听服务器集群机器上下线操作.

节点结构

每个节点称为一个 Znode。 每个 Znode 由 3 部分组成:

  • stat:此为状态信息, 描述该 Znode 的版本, 权限等信息
  • data:与该 Znode 关联的数据
  • children:该 Znode 下的子节点

节点类型

Znode 有 2 大类 4 小类, 两大类分别为永久节点和临时节点.

  • 永久节点(Persistent): 客户端和服务器端断开连接后,创建的节点不会消失, 只有在客户端执行删除操作的时候, 他们才能被删除.
  • 临时节点(Ephemeral): 客户端和服务器端断开连接后,创建的节点会被删除.

Znode 还有一个序列化的特性, 这个序列号对于此节点的父节点来说是唯一的, 这样便会记录每个子节点创建的先后顺序. 它的格式为“%10d”(10 位数字, 没有数值的数位用 0 补充, 例如“0000000001”),因此节点可以分为 4 小类:

  • 永久节点(Persistent)
  • 永久_序列化节点(Persistent_Sequential)
  • 临时节点(Ephemeral)
  • 临时_序列化节点(Ephemeral_Sequential)

Zookeeper 的 watch 监听机制

  • 在 ZooKeeper 中还支持一种 watch(监听)机制, 它允许对 ZooKeeper 注册监听, 当监听的对象发生指定的事件的时候, ZooKeeper 就会返回一个通知.
  • Watcher 分为以下三个过程:客户端向 ZK 服务端注册 Watcher、服务端事件发生触发 Watcher、客户端回调 Watcher 得到触发事件情况.
    触发事件种类很多,如:节点创建,节点删除,节点改变,子节点改变等。
  • Watcher 是一次性的. 一旦被触发将会失效. 如果需要反复进行监听就需要反复进行注册.

监听器原理

  • 首先要有一个 main()线程

  • 在 main 线程中创建 Zookeeper 客户端, 这时就会创建两个线程, 一个复制网络连接通信(connect), 一个负责监听(listener).

  • 通过 connect 线程将注册的监听事件发送给 zk, 常见的监听有

    监听节点数据的变化 get path [watch]

    监听节点状态的变化 stat path [watch]

    监听子节点增减的变化 ls path [watch]

  • 将注册的监听事件添加到 zk 的注册的监听器列表中

  • 监听到有数据或路径变化, 就会将这个消息发送给 listener 线程.

  • listener 线程内部调用了 process()方法.此方法是程序员自定义的方法, 里面可以写明监听到事件后做如何的通知操作.

监听器实际应用

  • 监听器+ZK 临时节点能够很好的监听服务器的上线和下线.
    1. 第一步: 先想 zk 集群注册一个监听器, 监听某一个节点路径
    2. 第二步: 主要服务器启动, 就去 zk 上指定路径下创建一个临时节点.
    3. 第三步: 监听器监听 servers 下面的子节点有没有变化, 一旦有变化, 不管新增(机器上线)还是减少(机器下线)都会马上给对应的人发送通知.

Zookeeper 的应用场景

ZK 提供的服务包括:

  • 统一命名服务,统一命名服务使用的是 ZK 的 node 节点全局唯一的这个特点.

    在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。例如:IP 不容易记住,而域名容易记住。创建一个节点后, 节点的路径就是全局唯一的, 可以作为全局名称使用.

  • 统一配置管理, 使用的是 Zookeeper 的 watch 机制

    可以把所有的配置都放在一个配置中心, 然后各个服务分别去监听配置中心,一旦发现里面的内容发生变化, 立即获取变化的内容, 然后更新本地配置即可.

    可将配置信息写入 Zookeeper 上的一个 Znode.

    各个客户端服务器监听这个 Znode.

    一旦 Znode 中的数据被修改, Zookeeper 将通知各个客户端服务器.

  • 统一集群管理,统一集群管理使用的是 Zookeeper 的 watch 机制

    可将节点信息写入 Zookeeper 上的一个 Znode

    监听这个 Znode 可获取它的实时状态变化

  • 集群选主,集群选主使用的是 zookeeper 的临时节点

    所有参与选主的主机都去 Zookeeper 上创建同一个临时节点,那么最终一定只有一个客户端请求能够 创建成功。

    成功创建节点的客户端所在的机器就成为了 Master,其他没有成功创建该节点的客户端,成为从节点

    所 有 的 从 节 点 都 会 在 主 节 点 上 注 册 一 个 子 节 点 变 更 的Watcher,用于监控当前主节点是否存活,一旦 发现当前的主节点挂了,那么其他客户端将会重新进行选主。

  • 服务动态上下线,

  • 分布式锁等.分布式锁使用的是 Zookeeper 的临时有序节点

    1. 所有需要执行操作的主机都去 Zookeeper 上创建一个临时有序节点.
    2. 然后获取到 Zookeeper 上创建出来的这些节点进行一个从小到大的排序.
    3. 判断自己创建的节点是不是最小的, 如果是, 自己就获取到了锁; 如果不是,则对最小的节点注册一个监听.
    4. 如果自己获取到了锁, 就去执行相应的操作. 当执行完毕之后, 连接断开, 节点消失, 锁就被释放了.
    5. 如果自己没有获取到锁, 就等待, 一直监听节点是否消失,锁被释放后, 再重新执行抢夺锁的操作.

Zookeeper 集群介绍

ookeeper 在一个系统中一般会充当一个很重要的角色, 所以一定要保证它的高可用,这就需要部署 Zookeeper 的集群. Zookeeper 有三种运行模式: 单机模式, 集群模式和伪集群模式.

  • 单机模式: 使用一台主机不是一个 Zookeeper 来对外提供服务, 有单点故障问题,仅适合于开发、测试环境.
  • 集群模式: 使用多台服务器, 每台上部署一个 Zookeeper 一起对外提供服务, 适合于生产环境.
  • 伪集群模式: 在服务器不够多的情况下, 也可以考虑在一台服务器上部署多个Zookeeper 来对外提供服务.

集群角色

  • Leader: 负责投票的发起和决议, 更新系统状态, 是事务请求(写请求) 的唯一处理者,一个 ZooKeeper 同一时刻只会有一个 Leader.
    对于 create 创建/setData 修改/delete 删除等有写操作的请求, 则需要统一转发给 leader处理, leader 需要决定编号和执行操作, 这个过程称为一个事务.
  • Follower: 接收客户端请求, 参与选主投票. 处理客户端非事务(读操作)请求,转发事务请求(写请求)给 Leader;
  • Observer: 针对访问量比较大的 zookeeper 集群, 为了增加并发的读请求. 还可新增观察者角色.作用: 可以接受客户端请求, 把请求转发给 leader, 不参与投票, 只同步 leader 的状态.

Zookeeper 集群的特性

  1. Zookeeper: 一个领导者(Leader), 多个跟随者(Follower)组成的集群.
  2. 集群中只要有半数以上节点存活, Zookeeper 集群就能正常服务.
  3. 全局数据一致: 每个 Server 保存一份相同的数据副本, Client 无论连接到哪个 Server, 数据都是一致的.
  4. 更新请求顺序性: 从同一个客户端发起的事务请求,最终会严格按照顺序被应用到zookeeper 中.
  5. 数据更新原子性: 一次数据更新要么成功, 要么失败。
  6. 实时性,在一定时间范围内,Client 能读到最新数据。

ZAB 协议

  • Zookeeper 采用 ZAB(Zookeeper Atomic Broadcast)协议来保证 分布式数据一致性 。
  • ZAB 并不是一种通用的分布式一致性算法,而是一种专为 Zookeeper 设计的崩溃可恢复的原子消息广播算法。
  • ZAB 协议包括两种基本模式: 崩溃恢复 模式和 消息广播 模式:
  • 消息广播模式主要用来进行事务请求的处理
  • 崩溃恢复模式主要用来在集群启动过程,或者 Leader 服务器崩溃退出后进行新的Leader 服务器的选举以及数据同步.

ZK 集群写数据流程

  1. Client 向 Zookeeper 的 Server1 上写数据, 发送一个写请求.
  2. 如果 Server1 不是 Leader, 那么 Server1 会把接受的请求进一步转发给 Leader, 因为每个 Zookeeper 的 Server 里面有一个是 Leader. 这个 Leader 会将写请求广播给各个Server, 比如 Server1 和 Server2, 各个 Server 会将该写请求加入待写队列, 并向Leader 发送成功信息(ack 反馈机制).
  3. 当 Leader 收到半数以上 Server 的成功信息, 说明该写操作可以执行. Leader 会向各个Server 发送事务提交信息, 各个 Server 收到信息后会落实队列里面的写请求, 此时写成功.
  4. Server1 会进一步通知 Client 数据写成功了, 这是就认为整个写操纵成功.

ZK 集群选举机制

  • Zookeeper 服务器有四个状态:
  1. looking: 寻找 leader 状态, 当服务器处于该状态时, 它会认为当前集群中没有 leader,因此需要进入 leader 选举状态.
  2. leading: 领导者状态, 表明当前服务器角色是 leader.
  3. following: 跟随者状态, 表明当前服务器角色是 follower.
  4. observing:观察者状态, 表明当前服务器角色是 observer。
  • 半数机制: 集群中半数以上机器存活, 集群可用, 所以 Zookeeper 适合安装奇数台服务器. 集群启动时, 如果当前机器票数超过了总票数一半则为 Leader, Leader 产生后, 投过票的机器就不能再投票了.
  • Zookeeper 虽然在配置文件中没有指定主从节点. 但是, Zookeeper 工作时, 是有一个节点 Leader, 其他则为 Follower, Leader 是通过内部的选举机制临时产生的.配置文件中会指定每台 ZK 的 myid, 而且不能重复, 通常用 1,2,3…区分每台 ZK 的myid.

集群启动器的选举机制

在集群初始化阶段, 当有一台服务器 server1 启动时, 其单独无法进行和完成 leader选举, 当第二台服务器 server2 启动时, 此时两台机器可以相互通信, 每台机器都试图找到leader, 于是进入 leader 选举过程.

  1. 服务器 1 启动, 服务器 1 状态保持为 looking.
  2. 服务器 2 启动, 发起一次选举. 服务器 1 投票给比自己 ID 号大的服务器 2. 服务器 2 投票给自己.
    投票结果: 服务器 1 票数 0 票, 服务器 2 票数 2 票, 没有半数以上结果, 选举无法完成,服务器 1, 2 状态保持 looking.
  3. 服务器 3 启动, 发起一次选举. 此时服务器 1 和 2 都会更改选票为服务器 3, 服务器 3 投票给自己.
    投票结果: 服务器 1 为 0 票, 服务器 2 为 0 票, 服务器 3 为 3 票. 此时服务器 3 的票数已经超过半数,服务器 3 当选 Leader. 服务器 1,2 更改状态为 follower,服务器 3更改状态为 leader;
  4. 服务器 4 启动, 发起一次选举. 此时服务器 1,2,3 已经不是 looking 状态, 不会更改选票信息, 服务器 4 投票给自己.
    投票结果:服务器 3 为 3 票,服务器 4 为 1 票。此时服务器 4 服从多数,更改选票信息为服务器 3,并更改状态为 following;
  5. 服务器 5 启动,同 4 一样当小弟.

服务器运行时期的 Leader 选举

在 zk 运行期间, leader 与非 leader 服务器各司其职, 即便当有非 leader 服务器宕机或者新加入, 此时也不会影响leader. 但是一旦leader服务器宕机了, 那么整个集群将会暂停对外服务, 进入新一轮 leader 选 举, 其过程和启动时期的 Leader 选举过程基本一致.

假设正在运行的有 server1,server2,server3 三台服务器,当前 leader 是 server2,若某一时刻 leader 挂了, 此时便开始 leader 选举. 选举过程如下:

  1. 变更转态, server1 和 server3 变更为 looking 状态.
  2. 开始投票, 每台服务器投票给比自己 myid 大的机器, 没有比自己大的就投给自己.
  3. 这样 server3有2票, server1有1票, server3的票数超过了集群一半, 当选 leader, server1 变更状态 follower.

为什么 zookeeper 集群的数目,一般为奇数个?

  1. 容错

    由于在增删改操作中需要半数以上服务器通过,来分析以下情况。2 台服务器,至少 2 台正常运行才行(2 的半数为 1,半数以上最少为 2),正常运行1 台服务器都不允许挂掉3 台服务器,至少 2 台正常运行才行(3 的半数为 1.5,半数以上最少为 2),正常运行可以允许 1 台服务器挂掉

    4 台服务器,至少 3 台正常运行才行(4 的半数为 2,半数以上最少为 3),正常运行可以允许 1 台服务器挂掉

    5 台服务器,至少 3 台正常运行才行(5 的半数为 2.5,半数以上最少为 3),正常运行可以允许 2 台服务器挂掉

    通过以上可以发现,3 台服务器和 4 台服务器都最多允许 1 台服务器挂掉,5 台服务器和 6 台服务器都最多允许 2 台服务器挂掉但是明显 4 台服务器成本高于 3 台服务器成本,6 台服务器成本高于 5 服务器成本。这是由于半数以上投票通过决定的。

  2. 防脑裂

    一个 zookeeper 集群中,可以有多个 follower.observer 服务器,但是必需只能有一个 leader 服务器。

集群互不通讯情况:

一个集群 3 台服务器,全部运行正常,但是其中 1 台裂开了,和另外 2 台无法通讯。3台机器里面 2 台正常运行过半票可以选出一个 leader。

一个集群 4 台服务器,全部运行正常,但是其中 2 台裂开了,和另外 2 台无法通讯。4台机器里面 2 台正常工作没有过半票以上达到 3,无法选出 leader 正常运行。

一个集群 5 台服务器,全部运行正常,但是其中 2 台裂开了,和另外 3 台无法通讯。5台机器里面 3 台正常运行过半票可以选出一个 leader。

一个集群 6 台服务器,全部运行正常,但是其中 3 台裂开了,和另外 3 台无法通讯。6台机器里面 3 台正常工作没有过半票以上达到 4,无法选出 leader 正常运行。

通过以上分析可以看出,为什么 zookeeper 集群数量总是单出现,主要原因还是在于第 2 点,防脑裂,对于第 1 点,无非是正本控制,但是不影响集群正常运行。但是出现第 2种裂的情况,zookeeper 集群就无法正常运行了。

CATALOG
  1. 1. Zookeeper 是什么
  2. 2. Zookeeper 的数据模型
  3. 3. 节点结构
  4. 4. 节点类型
  5. 5. Zookeeper 的 watch 监听机制
  6. 6. 监听器原理
  7. 7. 监听器实际应用
  8. 8. Zookeeper 的应用场景
  9. 9. Zookeeper 集群介绍
  10. 10. 集群角色
  11. 11. Zookeeper 集群的特性
  12. 12. ZAB 协议
  13. 13. ZK 集群写数据流程
  14. 14. ZK 集群选举机制
  15. 15. 集群启动器的选举机制
  16. 16. 服务器运行时期的 Leader 选举
  17. 17. 为什么 zookeeper 集群的数目,一般为奇数个?