其他
ZooKeeper的十二连问,你顶得了嘛?
The following article is from 捡田螺的小男孩 Author 捡田螺的小男孩
来源 | 捡田螺的小男孩
【前言】一线大厂ZooKeeper的十二连问,你顶得了嘛?
什么是Zookeeper
面试官:工作中使用过Zookeeper嘛?你知道它是什么,有什么用途呢?
有使用过的,使用ZooKeeper作为「dubbo的注册中心」,使用ZooKeeper实现「分布式锁」。 ZooKeeper,它是一个开放源码的「分布式协调服务」,它是一个集群的管理者,它将简单易用的接口提供给用户。 可以基于Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列「等功能」。 Zookeeper的「用途」:命名服务、配置管理、集群管理、分布式锁、队列管理
命名服务、配置管理、集群管理
面试官:说下什么是命名服务,什么是配置管理,又什么是集群管理吧。
「命名服务就是」: 命名服务是指通过「指定的名字」来获取资源或者服务地址。Zookeeper可以创建一个「全局唯一的路径」,这个路径就可以作为一个名字。被命名的实体可以是「集群中的机器,服务的地址,或者是远程的对象」等。一些分布式服务框架(RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据特定的名字来获取资源的实体、服务地址和提供者信息等。 「配置管理」 : 实际项目开发中,我们经常使用.properties或者xml需要配置很多信息,如数据库连接信息、fps地址端口等等。因为你的程序一般是分布式部署在不同的机器上(如果你是单机应用当我没说),如果把程序的这些配置信息「保存在zk的znode节点」下,当你要修改配置,即znode会发生变化时,可以通过改变zk中某个目录节点的内容,利用「watcher通知给各个客户端」,从而更改配置。 「集群管理」 集群管理包括集群监控和集群控制,其实就是监控集群机器状态,剔除机器和加入机器。zookeeper可以方便集群机器的管理,它可以实时监控znode节点的变化,一旦发现有机器挂了,该机器就会与zk断开连接,对用的临时目录节点会被删除,其他所有机器都收到通知。新机器加入也是类似酱紫,所有机器收到通知:有新兄弟目录加入啦。
zookeeper的数据模型
znode的4种类型
持久节点(PERSISTENT) 这类节点被创建后,就会一直存在于Zk服务器上。直到手动删除。 持久顺序节点(PERSISTENT_SEQUENTIAL) 它的基本特性同持久节点,不同在于增加了顺序性。父节点会维护一个自增整性数字,用于子节点的创建的先后顺序。 临时节点(EPHEMERAL) 临时节点的生命周期与客户端的会话绑定,一旦客户端会话失效(非TCP连接断开),那么这个节点就会被自动清理掉。zk规定临时节点只能作为叶子节点。 临时顺序节点(EPHEMERAL_SEQUENTIAL) 基本特性同临时节点,添加了顺序的特性。
znode节点里面存储的是什么?
public class DataNode implements Record {
byte data[];
Long acl;
public StatPersisted stat;
private Set<String> children = null;
}
「data:」 znode存储的业务数据信息 「ACL:」 记录客户端对znode节点的访问权限,如IP等。 「child:」 当前节点的子节点引用 「stat:」 包含Znode节点的状态信息,比如「事务id、版本号、时间戳」等等。
每个节点的数据最大不能超过多少呢?
Zookeeper watch机制
面试官:你知道znode节点上的监听机制嘛?讲下Zookeeper watch机制吧
Watcher机制 监听机制的工作原理 Watcher特性总结
Watcher监听机制
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方法。
Zookeeper的特性
面试官:你对Zookeeper的数据结构都有一定了解,那你讲下Zookeeper的特性吧。
「顺序一致性」:从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去。 「原子性」:所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。 「单一视图」:无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的。 「可靠性:」 一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来。 「实时性(最终一致性):」 Zookeeper 仅仅能保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。
zookeeper是如何保证事务的顺序一致性
面试官:你刚提到顺序一致性,那zookeeper是如何保证事务的顺序一致性的呢?
任期:完成本次选举后,直到下次选举前,由同一Leader负责协调写入; 事务计数器:单调递增,每生效一次写入,计数器加一。
Zookeeper 服务器角色
面试官:你提到了Leader,你知道Zookeeper的服务器有几种角色嘛?Zookeeper下Server工作状态又有几种呢?
Zookeeper 服务器角色
事务请求的唯一调度和处理者,保证集群事务处理的顺序性。 集群内部各服务的调度者。
处理客户端非事务请求,转发事务请求给Leader服务器。 参与事务请求Proposal的投票。 参与Leader选举投票。
处理客户端的非事务请求,转发事务请求给 Leader 服务器。 不参与任何形式的投票。
Zookeeper下Server工作状态
LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。 FOLLOWING:跟随者状态。表明当前服务器角色是Follower。 LEADING:领导者状态。表明当前服务器角色是Leader。 OBSERVING:观察者状态。表明当前服务器角色是Observer。
ZooKeeper集群部署图
面试官:你说到服务器角色是基于ZooKeeper集群的,那你画一下ZooKeeper集群部署图吧?ZooKeeper是如何保证主从节点数据一致性的呢?
ZooKeeper集群部署图
如果是写入数据,先写入主服务器(主节点),再通知从服务器。 如果是读取数据,既读主服务器的,也可以读从服务器的。
ZooKeeper如何保证主从节点数据一致性
「主服务器挂了,或者重启了」 「主从服务器之间同步数据」
崩溃恢复:Leader挂了,进入该模式,选一个新的leader出来。 消息广播:把更新的数据,从Leader同步到所有Follower。
ZooKeeper选举机制
面试官:Leader挂了,进入崩溃恢复,是如何选举Leader的呢?你讲一下ZooKeeper选举机制吧
服务器启动的Leader选举
服务器1(myid=1)启动,当前只有一台服务器,无法完成Leader选举。 服务器2(myid=2)启动,此时两台服务器能够相互通讯,开始进入Leader选举阶段。
每个服务器发出一个投票
接受来自各个服务器的投票
优先检查ZXID。ZXID比较大的服务器优先作为leader。 如果ZXID相同的话,就比较myid,myid比较大的服务器作为leader。服务器1的投票是(1,0),它收到投票是(2,0),两者zxid都是0,因为收到的myid=2,大于自己的myid=1,所以它更新自己的投票为(2,0),然后重新将投票发出去。对于服务器2呢,即不再需要更新自己的投票,把上一次的投票信息发出即可。
统计投票
服务器3(myid=3)启动,继续进入Leader选举阶段。
服务器4启动,发起一次选举。
服务器5启动,发起一次选举。
投票结束,服务器3当选为Leader
服务器运行期间的Leader选举
变更状态
每个服务器发起投票
接受来自各个服务器的投票 处理投票
统计投票 改变服务器状态
zk分布式锁的实现原理
面试官:你前面提到在项目中使用过Zookeeper的分布式锁,讲一下zk分布式锁的实现原理吧?
获取锁过程 (创建临时节点,检查序号最小) 释放锁 (删除临时节点,监听通知)
获取锁过程
当第一个客户端请求过来时,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节点是否存在。
释放锁
dubbo和Zookeeper的关系
面试官:好的,最后一道题,你说说dubbo和Zookeeper的关系吧,为什么选择Zookeeper作为注册中心?
命名服务,服务提供者向Zookeeper指定节点写入url,完成服务发布。 负载均衡,注册中心的承载能力有限,而Zookeeper集群配合web应用很容易达到负载均衡。 zk支持监听事件,特别适合发布/订阅的场景,dubbo的生产者和消费者就类似这场景。 数据模型简单,数据存在内存,可谓高性能。 Zookeeper其他特点都可以搬出来讲一下~
参考与感谢
<<从Paxos到Zookeeper 分布式一致性原理与实践>> zookeeper面试题[2] 28道进阶必备ZooKeeper面试真题(建议收藏!)[3] 漫画:什么是ZooKeeper?[4] 聊一聊ZooKeeper的顺序一致性[5] Zookeeper——一致性协议:Zab协议[6] Zookeeper的选举机制原理(图文深度讲解)[7] 漫画:如何用Zookeeper实现分布式锁?
Reference
更多阅读推荐