关于java:ZooKeeper的十二连问你顶得了嘛

32次阅读

共计 7758 个字符,预计需要花费 20 分钟才能阅读完成。

前言

一线大厂 ZooKeeper 的十二连问,你顶得了嘛?

本文曾经收录到 github

https://github.com/whx123/Jav…

1. 面试官:工作中应用过 Zookeeper 嘛?你晓得它是什么,有什么用处呢?

小菜鸡的我:

  • 有应用过的,应用 ZooKeeper 作为 dubbo 的注册核心,应用 ZooKeeper 实现 分布式锁
  • ZooKeeper,它是一个开放源码的 分布式协调服务,它是一个集群的管理者,它将简略易用的接口提供给用户。
  • 能够基于 Zookeeper 实现诸如数据公布 / 订阅、负载平衡、命名服务、分布式协调 / 告诉、集群治理、Master 选举、分布式锁和分布式队列 等性能
  • Zookeeper 的 用处:命名服务、配置管理、集群治理、分布式锁、队列治理

用处跟性能不是一个意思咩?给我一个眼神,让我本人领会

2. 面试官:说下什么是命名服务,什么是配置管理,又什么是集群治理吧

小菜鸡的我(幸好我刷过面试题),无所畏惧

    • 命名服务就是

    命名服务是指通过 指定的名字 来获取资源或者服务地址。Zookeeper 能够创立一个 全局惟一的门路 ,这个门路就能够作为一个名字。被命名的实体能够是 集群中的机器,服务的地址,或者是近程的对象 等。一些分布式服务框架(RPC、RMI)中的服务地址列表,通过应用命名服务,客户端利用可能依据特定的名字来获取资源的实体、服务地址和提供者信息等。

    • 配置管理:

    理论我的项目开发中,咱们常常应用.properties 或者 xml 须要配置很多信息,如数据库连贯信息、fps 地址端口等等。因为你的程序个别是分布式部署在不同的机器上(如果你是单机利用当我没说),如果把程序的这些配置信息 保留在 zk 的 znode 节点 下,当你要批改配置,即 znode 会发生变化时,能够通过扭转 zk 中某个目录节点的内容,利用watcher 告诉给各个客户端,从而更改配置。

    • 集群治理

    集群治理包含集群监控和集群管制,其实就是监控集群机器状态,剔除机器和退出机器。zookeeper 能够不便集群机器的治理,它能够实时监控 znode 节点的变动,一旦发现有机器挂了,该机器就会与 zk 断开连接,对用的长期目录节点会被删除,其余所有机器都收到告诉。新机器退出也是相似酱紫,所有机器收到告诉:有新兄弟目录退出啦。

    3. 面试官:你提到了 znode 节点,那你晓得 znode 有几种类型呢?zookeeper 的数据模型是怎么的呢?

    小菜鸡的我(我先想想):

    zookeeper 的数据模型

    ZooKeeper 的视图数据结构,很像 Unix 文件系统,也是树状的,这样能够确定每个门路都是惟一的。zookeeper 的节点对立叫做 znode,它是能够通过 门路来标识,结构图如下:

    znode 的 4 种类型

    依据节点的生命周期,znode 能够分为 4 种类型,别离是长久节点(PERSISTENT)、长久程序节点(PERSISTENT_SEQUENTIAL)、长期节点(EPHEMERAL)、长期程序节点(EPHEMERAL_SEQUENTIAL)

    • 长久节点(PERSISTENT)

    这类节点被创立后,就会始终存在于 Zk 服务器上。直到手动删除。

    • 长久程序节点(PERSISTENT_SEQUENTIAL)

    它的根本个性同长久节点,不同在于减少了程序性。父节点会保护一个自增整性数字,用于子节点的创立的先后顺序。

    • 长期节点(EPHEMERAL)

    长期节点的生命周期与客户端的会话绑定,一旦客户端会话生效(非 TCP 连贯断开),那么这个节点就会被主动清理掉。zk 规定长期节点只能作为叶子节点。

    • 长期程序节点(EPHEMERAL_SEQUENTIAL)

    根本个性同长期节点,增加了程序的个性。

    4、面试官:你晓得 znode 节点外面存储的是什么吗?每个节点的数据最大不能超过多少呢?

    小菜鸡的我:

    znode 节点外面存储的是什么?

    Znode 数据节点的代码如下

    public class DataNode implements Record {byte data[];                    
        Long acl;                       
        public StatPersisted stat;       
        private Set<String> children = null; 
    }

    哈哈,Znode 蕴含了 存储数据、拜访权限、子节点援用、节点状态信息,如图:

    • data: znode 存储的业务数据信息
    • ACL: 记录客户端对 znode 节点的拜访权限,如 IP 等。
    • child: 以后节点的子节点援用
    • stat: 蕴含 Znode 节点的状态信息,比方 事务 id、版本号、工夫戳 等等。

    每个节点的数据最大不能超过多少呢

    为了保障高吞吐和低提早,以及数据的一致性,znode 只适宜存储十分小的数据,不能超过 1M,最好都小于 1K。

    5、面试官:你晓得 znode 节点上的监听机制嘛?讲下 Zookeeper watch 机制吧。

    小菜鸡的我:

    • Watcher 机制
    • 监听机制的工作原理
    • Watcher 个性总结

    Watcher 监听机制

    Zookeeper 容许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件告诉来实现分布式的告诉性能,而后客户端依据 Watcher 告诉状态和事件类型做出业务上的扭转。

    能够把 Watcher 了解成客户端注册在某个 Znode 上的触发器,当这个 Znode 节点发生变化时(增删改查),就会触发 Znode 对应的注册事件,注册的客户端就会收到异步告诉,而后做出业务的扭转。

    Watcher 监听机制的工作原理

    • ZooKeeper 的 Watcher 机制次要包含客户端线程、客户端 WatcherManager、Zookeeper 服务器三局部。
    • 客户端向 ZooKeeper 服务器注册 Watcher 的同时,会将 Watcher 对象存储在客户端的 WatchManager 中。
    • 当 zookeeper 服务器触发 watcher 事件后,会向客户端发送告诉,客户端线程从 WatcherManager 中取出对应的 Watcher 对象来执行回调逻辑。

    Watcher 个性总结

    • 一次性:一个 Watch 事件是一个一次性的触发器。一次性触发,客户端只会收到一次这样的信息。
    • 异步的: Zookeeper 服务器发送 watcher 的告诉事件到客户端是异步的,不能冀望可能监控到节点每次的变动,Zookeeper 只能保障最终的一致性,而无奈保障强一致性。
    • 轻量级: Watcher 告诉非常简单,它只是告诉产生了事件,而不会传递事件对象内容。
    • 客户端串行: 执行客户端 Watcher 回调的过程是一个串行同步的过程。
    • 注册 watcher 用 getData、exists、getChildren 办法
    • 触发 watcher 用 create、delete、setData 办法

    6、面试官:你对 Zookeeper 的数据结构都有肯定理解,那你讲下 Zookeeper 的个性吧

    小菜鸡的我:(我背过书,啊哈哈)

    Zookeeper 保障了如下分布式一致性个性:

    • 程序一致性:从同一客户端发动的事务申请,最终将会严格地依照程序被利用到 ZooKeeper 中去。
    • 原子性:所有事务申请的处理结果在整个集群中所有机器上的利用状况是统一的,也就是说,要么整个集群中所有的机器都胜利利用了某一个事务,要么都没有利用。
    • 繁多视图:无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是统一的。
    • 可靠性: 一旦服务端胜利地利用了一个事务,并实现对客户端的响应,那么该事务所引起的服务端状态变更将会被始终保留下来。
    • 实时性(最终一致性): Zookeeper 仅仅能保障在肯定的时间段内,客户端最终肯定可能从服务端上读取到最新的数据状态。

    7、面试官:你刚提到程序一致性,那 zookeeper 是如何保障事务的程序一致性的呢?

    小菜鸡的我:(完蛋了这题不会)

    这道题能够看下这篇文章(本题答案来自该文章):聊一聊 ZooKeeper 的程序一致性

    须要理解事务 ID,即 zxid。ZooKeeper 的在选举时通过比拟各结点的 zxid 和机器 ID 选出新的主结点的。zxid 由 Leader 节点生成,有新写入事件时,Leader 生成新 zxid 并随提案一起播送,每个结点本地都保留了以后最近一次事务的 zxid,zxid 是递增的,所以谁的 zxid 越大,就示意谁的数据是最新的。

    ZXID 的生成规定如下:

    ZXID 有两局部组成:

    • 任期:实现本次选举后,直到下次选举前,由同一 Leader 负责协调写入;
    • 事务计数器:枯燥递增,每失效一次写入,计数器加一。

    ZXID 的低 32 位是计数器,所以同一任期内,ZXID 是间断的,每个结点又都保留着本身最新失效的 ZXID,通过比照新提案的 ZXID 与本身最新 ZXID 是否相差“1”,来保障事务严格依照程序失效的。

    8、面试官:你提到了 Leader,你晓得 Zookeeper 的服务器有几种角色嘛?Zookeeper 下 Server 工作状态又有几种呢?

    小菜鸡的我:

    Zookeeper 服务器角色

    Zookeeper 集群中,有 Leader、Follower 和 Observer 三种角色

    Leader

    Leader 服务器是整个 ZooKeeper 集群工作机制中的外围,其次要工作:

    • 事务申请的惟一调度和解决者,保障集群事务处理的程序性
    • 集群外部各服务的调度者

    Follower

    Follower 服务器是 ZooKeeper 集群状态的跟随者,其次要工作:

    • 解决客户端非事务申请,转发事务申请给 Leader 服务器
    • 参加事务申请 Proposal 的投票
    • 参加 Leader 选举投票

    Observer

    Observer 是 3.3.0 版本开始引入的一个服务器角色,它充当一个观察者角色——察看 ZooKeeper 集群的最新状态变动并将这些状态变更同步过去。其工作:

    • 解决客户端的非事务申请,转发事务申请给 Leader 服务器
    • 不参加任何模式的投票

    Zookeeper 下 Server 工作状态

    服务器具备四种状态,别离是 LOOKING、FOLLOWING、LEADING、OBSERVING。

    • 1.LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为以后集群中没有 Leader,因而须要进入 Leader 选举状态。
    • 2.FOLLOWING:跟随者状态。表明以后服务器角色是 Follower。
    • 3.LEADING:领导者状态。表明以后服务器角色是 Leader。
    • 4.OBSERVING:观察者状态。表明以后服务器角色是 Observer。

    9、面试官:你说到服务器角色是基于 ZooKeeper 集群的,那你画一下 ZooKeeper 集群部署图吧?ZooKeeper 是如何保障主从节点数据一致性的呢?

    小菜鸡的我:

    ZooKeeper 集群部署图


    ZooKeeper 集群是一主多从的构造:

    • 如果是写入数据,先写入主服务器(主节点),再告诉从服务器。
    • 如果是读取数据,既读主服务器的,也能够读从服务器的。

    ZooKeeper 如何保障主从节点数据一致性

    咱们晓得集群是主从部署构造,要保障主从节点一致性问题,无非就是两个次要问题:

    • 主服务器挂了,或者重启了
    • 主从服务器之间同步数据~

    Zookeeper 是采纳 ZAB 协定(Zookeeper Atomic Broadcast,Zookeeper 原子播送协定)来保障主从节点数据一致性的,ZAB 协定反对 解体复原和音讯播送 两种模式,很好解决了这两个问题:

    • 解体复原:Leader 挂了,进入该模式,选一个新的 leader 进去
    • 音讯播送:把更新的数据,从 Leader 同步到所有 Follower

    Leader 服务器挂了,所有集群中的服务器进入 LOOKING 状态,首先,它们会选举产生新的 Leader 服务器;接着,新的 Leader 服务器与集群中 Follower 服务进行数据同步,当集群中超过半数机器与该 Leader 服务器实现数据同步之后,退出恢复模式进入音讯播送模式。Leader 服务器开始接管客户端的事务申请生成事务 Proposal 进行事务申请解决。

    10、面试官:Leader 挂了,进入解体复原,是如何选举 Leader 的呢?你讲一下 ZooKeeper 选举机制吧

    小菜鸡的我:

    服务器启动或者服务器运行期间(Leader 挂了),都会进入 Leader 选举,咱们来看一下~ 假如当初 ZooKeeper 集群有五台服务器,它们 myid 别离是服务器 1、2、3、4、5,如图:

    服务器启动的 Leader 选举

    zookeeper 集群初始化阶段,服务器(myid=1-5)顺次 启动,开始 zookeeper 选举 Leader~

    • 服务器 1(myid=1)启动,以后只有一台服务器,无奈实现 Leader 选举
    • 服务器 2(myid=2)启动,此时两台服务器可能互相通信,开始进入 Leader 选举阶段
    1. 每个服务器收回一个投票

    服务器 1 和 服务器 2 都将本人作为 Leader 服务器进行投票,投票的根本元素包含:服务器的 myid 和 ZXID,咱们以(myid,ZXID)模式示意。初始阶段,服务器 1 和服务器 2 都会投给本人,即服务器 1 的投票为(1,0),服务器 2 的投票为(2,0),而后各自将这个投票发给集群中的其余所有机器。

    1. 承受来自各个服务器的投票

    每个服务器都会承受来自其余服务器的投票。同时,服务器会校验投票的有效性,是否本轮投票、是否来自 LOOKING 状态的服务器。

    1. 解决投票

    收到其余服务器的投票,会将被人的投票跟本人的投票 PK,PK 规定如下:

    • 优先查看 ZXID。ZXID 比拟大的服务器优先作为 leader。
    • 如果 ZXID 雷同的话,就比拟 myid,myid 比拟大的服务器作为 leader。

    服务器 1 的投票是(1,0),它收到投票是(2,0),两者 zxid 都是 0,因为收到的 myid=2,大于本人的 myid=1,所以它更新本人的投票为(2,0),而后从新将投票收回去。对于服务器 2 呢,即不再须要更新本人的投票,把上一次的投票信息收回即可。

    1. 统计投票

    每次投票后,服务器会统计所有投票,判断是否有过半的机器承受到雷同的投票信息。服务器 2 收到两票,少于 3(n/2+1,n 为总服务器),所以持续放弃 LOOKING 状态

    • 服务器 3(myid=3)启动,持续进入 Leader 选举阶段

    跟后面流程统一,服务器 1 和 2 先投本人一票,因为服务器 3 的 myid 最大,所以大家把票改投给它。此时,服务器为 3 票(大于等于 n /2+1), 所以服务器 3 入选为 Leader。服务器 1,2 更改状态为 FOLLOWING,服务器 3 更改状态为 LEADING;

    • 服务器 4 启动,发动一次选举。

    此时服务器 1,2,3 曾经不是 LOOKING 状态,不会更改选票信息。选票信息后果:服务器 3 为 4 票,服务器 4 为 1 票。服务器 4 并更改状态为 FOLLOWING;

    • 服务器 5 启动,发动一次选举。

    同理,服务器也是把票投给服务器 3,服务器 5 并更改状态为 FOLLOWING;

    • 投票完结,服务器 3 入选为 Leader

    服务器运行期间的 Leader 选举

    zookeeper 集群的五台服务器(myid=1-5)正在运行中,忽然某个霎时,Leader 服务器 3 挂了,这时候便开始 Leader 选举~

    • 1. 变更状态

    Leader 服务器挂了之后,余下的非 Observer 服务器都会把本人的服务器状态更改为 LOOKING,而后开始进入 Leader 选举流程。

    • 2. 每个服务器发动投票

    每个服务器都把票投给本人,因为是运行期间,所以每台服务器的 ZXID 可能不雷同。假如服务 1,2,4,5 的 zxid 别离为 333,666,999,888,则别离产生投票(1,333),(2,666),(4,999)和(5,888),而后各自将这个投票发给集群中的其余所有机器。

    • 3. 承受来自各个服务器的投票
    • 4. 解决投票

    投票规定是跟 Zookeeper 集群启动期间统一的,优先查看 ZXID,大的优先作为 Leader,所以显然服务器 zxid=999 具备优先权。

    • 5. 统计投票
    • 6. 扭转服务器状态

    11、面试官:你后面提到在我的项目中应用过 Zookeeper 的分布式锁,讲一下 zk 分布式锁的实现原理吧?

    小菜鸡的我:

    Zookeeper 就是应用长期程序节点个性实现分布式锁的。

    • 获取锁过程(创立长期节点,查看序号最小)
    • 开释锁(删除长期节点,监听告诉)

    获取锁过程

    • 当第一个客户端申请过去时,Zookeeper 客户端会创立一个长久节点 /locks。如果它(Client1)想取得锁,须要在 locks 节点下创立一个程序节点 lock1. 如图

    • 接着,客户端 Client1 会查找 locks 上面的所有长期程序子节点,判断本人的节点 lock1 是不是排序最小的那一个,如果是,则胜利取得锁。

    • 这时候如果又来一个客户端 client2 前来尝试取得锁,它会在 locks 下再创立一个长期节点 lock2

    • 客户端 client2 一样也会查找 locks 上面的所有长期程序子节点,判断本人的节点 lock2 是不是最小的,此时,发现 lock1 才是最小的,于是获取锁失败。获取锁失败,它是不会甘心的,client2 向它排序靠前的节点 lock1 注册 Watcher 事件,用来监听 lock1 是否存在,也就是说 client2 抢锁失败进入期待状态。

    • 此时,如果再来一个客户端 Client3 来尝试获取锁,它会在 locks 下再创立一个长期节点 lock3

    • 同样的,client3 一样也会查找 locks 上面的所有长期程序子节点,判断本人的节点 lock3 是不是最小的,发现自己不是最小的,就获取锁失败。它也是不会甘心的,它会向在它后面的节点 lock2 注册 Watcher 事件,以监听 lock2 节点是否存在。

    开释锁

    咱们再来看看开释锁的流程,zookeeper 的 客户端业务实现或者故障 ,都会删除长期节点,开释锁。如果是工作实现,Client1 会显式调用删除 lock1 的指令

    如果是客户端故障了,依据长期节点得个性,lock1 是会主动删除的

    lock1 节点被删除后,Client2 可开心了,因为它始终监听着 lock1。lock1 节点删除,Client2 立即收到告诉,也会查找 locks 上面的所有长期程序子节点,发下 lock2 是最小,就取得锁。

    同理,Client2 取得锁之后,Client3 也对它虎视眈眈,啊哈哈~

    12. 面试官:好的,最初一道题,你说说 dubbo 和 Zookeeper 的关系吧,为什么抉择 Zookeeper 作为注册核心?

    小菜鸡的我(答了这么多道题,不会还不给我过吧?):

    dubbo 的注册核心能够选 Zookeeper,memcached,redis 等。为什么抉择 Zookeeper,因为它的性能个性咯~

    • 命名服务,服务提供者向 Zookeeper 指定节点写入 url,实现服务公布。
    • 负载平衡,注册核心的承载能力无限,而 Zookeeper 集群配合 web 利用很容易达到负载平衡。
    • zk 反对监听事件,特地适宜公布 / 订阅的场景,dubbo 的生产者和消费者就相似这场景。
    • 数据模型简略,数据存在内存,堪称高性能
    • Zookeeper 其余特点都能够搬出来讲一下~

    集体公众号

    参考与感激

    • << 从 Paxos 到 Zookeeper 分布式一致性原理与实际 >>
    • zookeeper 面试题
    • 28 道进阶必备 ZooKeeper 面试真题(倡议珍藏!)
    • 漫画:什么是 ZooKeeper?
    • 聊一聊 ZooKeeper 的程序一致性
    • Zookeeper——一致性协定:Zab 协定
    • Zookeeper 的选举机制原理(图文深度解说)
    • 漫画:如何用 Zookeeper 实现分布式锁?

    正文完
     0