查看原文
其他

分布式面试【zookeeper】

田老师 Java后端技术全栈 2021-08-29

欢迎关注Java后端技术全栈

ZooKeeper

1. ZooKeeper 是什么?

直译:从名字上直译就是动物管理员,动物指的是 Hadoop 一类的分布式软件,管理员三个字体现了 ZooKeeper 的特点:维护、协调、管理、监控。

简述:有些软件你想做成集群或者分布式,你可以用 ZooKeeper 帮你来辅助实现。

特点:

  • 最终一致性:客户端看到的数据最终是一致的。

  • 可靠性:服务器保存了消息,那么它就一直都存在。

  • 实时性:ZooKeeper 不能保证两个客户端同时得到刚更新的数据。

  • 独立性(等待无关):不同客户端直接互不影响。

  • 原子性:更新要不成功要不失败,没有第三个状态。

注意:回答面试题,切忌只是简单一句话回答,可以将你对概念的理解,特点等多个方面描述一下,哪怕你自己认为不完全切中题意的也可以说说,面试官不喜欢会打断你的,你的目的是让面试官认为你是好沟通的。当然了,如果不会可别装作会,说太多不专业的想法。


2. 描述一下 ZAB 协议

ZAB 协议是 ZooKeeper 自己定义的协议,全名 ZooKeeper 原子广播协议。

ZAB 协议有两种模式:Leader 节点崩溃了如何恢复和消息如何广播到所有节点。

整个 ZooKeeper 集群没有 Leader 节点的时候,属于崩溃的情况。比如集群启动刚刚启动,这时节点们互相不认识。比如运作 Leader 节点宕机了,又或者网络问题,其他节点 Ping 不通 Leader 节点了。这时就需要 ZAB 中的节点崩溃协议,所有节点进入选举模式,选举出新的 Leader。整个选举过程就是通过广播来实现的。选举成功后,一切都需要以 Leader 的数据为准,那么就需要进行数据同步了。

3. 四种类型的数据节点 Znode

  • 持久节点:和我们存储到数据库的情况一样,存上了就不会丢失。

  • 临时节点:你通过 ZK 客户端远程连接到 ZK 服务端,创建了临时节点,等待你的连接超时了,对不起这个节点就删除了,这就是临时节点。

  • 持久顺序节点:首先它是持久化的,然后如果你创建了同名的节点,它不会说节点也存在,而且在名字后加上后缀。就像 Windows 的创建文件夹一样,后缀是数字从小到大,所以也就有了顺序性。

  • 临时顺序节点:临时节点和顺序节点上面都解释过了,没错,就是它俩的组合,客户端连接超时节点就会消失,同名的节点后缀是排序的。

4. TCP 不是可靠连接吗,为什么分布式要考虑网络信息丢失的问题?

TCP 协议只能保证 TCP 层的可靠,所以数据到了应用层就不受控制了,而且我们需要的是应用层的可靠性。

看下图一目了然:

看到上图,这是在一台机器内部,可能传输出问题的概率不高,还有第二个原因:

TCP 协议只能保证同一个 TCP 连接内的消息是有序的。但在分布式系统中,发送数据,可能多个 TCP 连接发送一起发生一段数据,这个不同 TCP 连接的数据顺序 TCP 协议不会保证的,应用层协议也不保证,只能我们代码实现时控制。

看图 2:

5. 介绍一下两阶段提交协议 2PC

第一阶段:

  • 协调者问所有参与者你那里是否能够提交数据,不管能不能都告诉我结果;

  • 参与者收到数据就开始执行,将 Undo 和 Redo 信息写入日志,执行但是没有真正提交,等下一步操作再做最终处理,现在的情况是可进可退。

  • 每个参与者都把结果如实告诉协调者。

第二阶段:当协调者从所有参与者获得的相应消息都为同意时:

  • 协调者向所有参与者发出开干的命令;

  • 参与者完成最终的数据操作;

  • 参与者告诉协调者节点一定都搞定了;

  • 协调者得到所有参与者节点的好消息,这才算是完成事务。

如果执行失败呢,也是一样的回滚流程。

两阶段提交看似不错,但是有个阻塞的问题,这个问题 两阶段无法解决,需要三阶段来解决。

6. 介绍一下三阶段提交协议 3PC

三阶段提交针对两阶段提交有两个改动点:

  • 引入超时。如果等待时间过长那就超过了,不会一直等下去,解决了如果出现阻塞的麻烦。

  • 多了一个准备阶段。一些可能出现的地方放到准备阶段了,这也是名字的由来。

第一阶段

2PC 的准备阶段很像。协调者向参与者发送处理数据的请求,参与者如果如果做好了就给好消息,搞砸了就给坏消息。

第二阶段

协调者根据参与者的好消息们来判断是否可以继续事务的 预提交操作。

如果都是好消息,那么就会执行事务的预执行。

  • 发送请求:向参与者发送预提交请求。

  • 预提交:参与者接收到预提交请求后,会执行事务操作,并将 Undo 和 Redo 信息记录到事务日志中。

  • 反馈:如果参与者成功的执行了事务操作,继续返回好消息,然后等待最终指令。

万一有谁向协调者报告了坏消息,或者超时了,协调者都没有接到参与者的消息,那么就执行事务的中断。

  • 发送请求:协调者通知所有参与者中断事务。

  • 中断事务:参与者收到中断请求之后,中断事务。

第三阶段最终的事务提交:

执行提交

  • 协调者发送请求:协调通知所有参与者真正的提交事务请求。

  • 参与者事务提交:参与者接收到真正提交事务的请求之后,执行正式的事务提交。

  • 参与者反馈:事务提交后,告诉协调者好消息。

  • 协调者完成事务:协调者接收到所有参与者的好消息之后,完成事务。

中断事务

规定时间内,协调者收到的好消息数量不够,那么就会执行中断事务。

  • 协调者发送中断请求:协调者向所有参与者发送中断事务请求。

  • 参与者事务回滚:参与者接收到中断事务请求之后,利用其在阶段二记录的 Undo 信息来执行事务的回滚操作。

  • 参与者反馈:事务回滚之后,向协调者发送 回滚完了。

  • 协调者中断事务:协调者接收到参与者反馈的回滚完成的消息之后,执行事务的中断。

三阶段提交的问题:

网络分区可能会带来问题。但是很少会有人再提四阶段了,事实上大家一般用 TCC 的思想来解决分布式事务的问题。

7. ZooKeeper 宕机如何处理?

ZooKeeper 本身也是集群,推荐配置奇数个服务器。因为宕机就需要选举,选举需要半数 +1 票才能通过,为了避免打成平手。进来不用偶数个服务器。

如果是 Follower 宕机了,没关系不影响任何使用。用户无感知。如果 Leader 宕机,集群就得停止对外服务,开始选举,选举出一个 Leader 节点后,进行数据同步,保证所有节点数据和 Leader 统一,然后开始对外提供服务。

为啥投票需要半数 +1,如果半数就可以的话,网络的问题可能导致集群选举出来两个 Leader,各有一半的小弟支持,这样数据也就乱套了。

8. 描述一下 ZooKeeper 的 session 管理的思想?

分桶策略:

简单地说,就是不同的会话过期可能都有时间间隔,比如 15 秒过期、15.1 秒过期、15.8 秒过期,ZooKeeper 统一让这些 session 16 秒过期。这样非常方便管理,看下面的公式,过期时间总是 ExpirationInterval 的整数倍。

计算公式:

ExpirationTime = currentTime + sessionTimeout
ExpirationTime = (ExpirationTime / ExpirationInrerval + 1) * ExpirationInterval ,

见图片:

默认配置的 session 超时时间是在 2tickTime~20tickTime。

9. ZooKeeper Watcher 机制

监听是设计模式的一种思想,监听的节点发生了变化,监听者就会知道。一定按钮事件,异步通知都采用了类似的思想。

ZK 保存的节点,如果发生了变化,怎么通知使用者呢?可以通过 Watcher 机制。

整个使用流程:

客户端要先注册监听,指明要监听的节点。服务端把这些信息保存,操作节点时,检查是否有监听信息,有的话,就通知客户端。

注意,ZK Watcher 有个特点就是一次性的,这样看上去使用很麻烦。但是节省了资源,否则监听太多,或者监听一次就不用了,但是 ZK 还一遍一遍地通知是不合适的。一次性就表示有需要就注册。

10. ZooKeeper Server 的角色

Leader:

  • 整个集群就一个,修改数据的操作只有 Leader 能执行,执行完同步给整个集群。

Follower:

  • 处理客户端的读请求,也就是不改变数据的都可以处理,改变数据的转发请求给 Leader 服务器。

  • 如果 Leader 宕机了,参与选举,自己可以给自己投票。

Observer:

  • 处理客户端的读请求,也就是不改变数据的都可以处理,改变数据的转发请求给 Leader 服务器。这点和 Follower 很像。

  • 不能投票,没有选举权和被选举权。

11. ZooKeeper Server 的状态

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

  • LOOKING:集群宕机了,无法对外提供服务了,这时需要选举,所有节点都是这个状态。

  • FOLLOWING:Follower 节点正常情况下就是这个状态。

  • LEADING:Leader 节点正常情况下就是这个状态。

  • OBSERVING:Observer 节点正常情况下是这个状态。

对比三种角色,你会发现多了一种状态 Looking,这是在选举时大家的状态,表明需要选举,无法正常工作。

12. ZooKeeper 负载均衡和 Nginx 负载均衡区别

ZooKeeper:

  • 不存在单点问题,zab 机制保证单点故障可重新选举一个 Leader

  • 只负责服务的注册与发现,不负责转发,减少一次数据交换(消费方与服务方直接通信)

  • 需要自己实现相应的负载均衡算法

Nginx:

  • 存在单点问题,单点负载高数据量大,需要通过 KeepAlived 辅助实现高可用

  • 每次负载,都充当一次中间人转发角色,本身是个反向代理服务器

  • 自带负载均衡算法

13. ZooKeeper 的序列化

序列化:

  • 内存数据,保存到硬盘需要序列化。

  • 内存数据,通过网络传输到其他节点,需要序列化。

ZK 使用的序列化协议是 Jute,Jute 提供了 Record 接口。接口提供了两个方法:

  • serialize 序列化方法

  • deserialize 反序列化方法

要系列化的方法,在这两个方法中存入到流对象中即可。

14. Zxid 是什么,有什么作用

Zxid,也就是事务 id,为了保证事务的顺序一致性,ZooKeeper 采用了递增的事务 Zxid 来标识事务。proposal 都会加上了 Zxid。Zxid 是一个 64 位的数字,它高 32 位是 Epoch 用来标识朝代变化,比如每次选举 Epoch 都会加改变。低 32 位用于递增计数。

Epoch:可以理解为当前集群所处的年代或者周期,每个 Leader 就像皇帝,都有自己的年号,所以每次改朝换代,Leader 变更之后,都会在前一个年代的基础上加 1。这样就算旧的 Leader 崩溃恢复之后,也没有人听它的了,因为 Follower 只听从当前年代的 Leader 的命令。

15. 讲解一下 ZooKeeper 的持久化机制

什么是持久化?

  • 数据,存到磁盘或者文件当中。

  • 机器重启后,数据不会丢失。内存 -> 磁盘的映射,和序列化有些像。

ZooKeeper 的持久化:

  • SnapShot 快照,记录内存中的全量数据

  • TxnLog 增量事务日志,记录每一条增删改记录(查不是事务日志,不会引起数据变化)

为什么持久化这么麻烦,一个不可用吗?

快照的缺点,文件太大,而且快照文件不会是最新的数据。增量事务日志的缺点,运行时间长了,日志太多了,加载太慢。二者结合最好。

快照模式:

  • 将 ZooKeeper 内存中以 DataTree 数据结构存储的数据定期存储到磁盘中。

  • 由于快照文件是定期对数据的全量备份,所以快照文件中数据通常不是最新的。

见图片:


16. 投票信息的五元组

  • Leader:被选举的 Leader 的 SID

  • Zxid:被选举的 Leader 的事务 ID

  • Sid:当前服务器的 SID

  • electionEpoch:当前投票的轮次

  • peerEpoch:当前服务器的 Epoch

Epoch > Zxid > Sid

Epoch,Zxid 都可能一致,但是 Sid 一定不一样,这样两张选票一定会 PK 出结果。

17. Quorum 与脑裂

当某选票,占参与选举的数量的一半以上,选举结果

脑裂:大脑裂开了,成了两个大脑。这时数据不一致,客户端访问的结果就很佛系了。

半数以上的选票就是解决之道,只有拿到大于半数的选票才能成为大脑。

18. 选举的全过程

两种情况会出现选举:

  1. 服务器们启动的时候

  2. 服务器运行过程中,Leader 失联了

服务器们启动的选举:

三台服务器 server1、server2、server3:

1.server1 启动,一台机器不会选举。

2. server2 启动,server1 和 server2 的状态改为 looking,广播投票

3. server3 启动,状态改为 looking,加入广播投票。

4. 初识状态,互不认识,大家都认为自己是王者,投票也投自己为 Leader。

5. 投票信息说明,票信息本来为五元组,这里为了逻辑清晰,简化下表达。

初识 zxid = 0,sid 是每个节点的名字,这个 sid 在 zoo.cfg 中配置,不会重复。

节点sid
server11
server22
server33

6. 初始 zxid=0,server1 投票(1,0),server2 投票(2,0),server3 投票(3,0)

7. server1 收到 投票(2,0)时,会先验证投票的合法性,然后自己的票进行 pk,pk 的逻辑是先比较 zxid,server1(zxid)=server2(zxid)=0,zxid 相等再比较 sid,server1(sid)< server2(sid),pk 结果为 server2 的投票获胜。server1 更新自己的投票为 (2,0),server1 重新投票。

8. TODO 这里最终是 2 还是 3,需要做实验确定。

9. server2 收到 server1 投票,会先验证投票的合法性,然后 pk,自己的票获胜,server 不用更新自己的票,pk 后,重新在发送一次投票。

10. 统计投票,pk 后会统计投票,如果半数以上的节点投出相同的票,确定选出了 Leader。

11. 选举结束,被选中节点的状态由 LOOKING 变成 LEADING,其他参加选举的节点由 LOOKING 变成 FOLLOWING。如果有 Observer 节点,如果 Observer 不参与选举,所以选举前后它的状态一直是 OBSERVING,没有变化。

简单地说

开始投票 -> 节点状态变成 LOOKING -> 每个节点选自己-> 收到票进行 PK -> sid 大的获胜 -> 更新选票 -> 再次投票 -> 统计选票,选票过半数选举结果 -> 节点状态更新为自己的角色状态。

19. 数据同步全过程

数据同步,麻烦的地方在于有不同的情况。选举结束后的首要工作就是数据同步。Learner 服务器会发送给 Leader 服务器一个数据包,其中的 lastZxid 会表明自己的数据新旧程度。

这里需要三个变量,辅助判断:

  • peerLastZxid:Follower 最后处理的 Zxid

  • minCommittedLog:Leader 缓存队列中的最小 Zxid

  • maxCommittedLog:Leader 缓存队列中的最大 Zxid

1. DIFF 同步

peerLastZxid > minCommittedLog && peerLastZxid < maxCommittedLog

这种情况,就是 Follower 少了一部分数据,直接同步 Leader 即可。

2. TRUNC 同步

peerLastZxid > maxCommittedLog

Follower 的数据比 Leader 多,但是一切要以 Leader 为准,这就需要回滚。多余的数据删除掉。

3. 全量同步(SNAP 同步)

peerLastZxid < minCommittedLog

这时 Follower 已经无法依赖队列的数据进行同步,因为它缺少的不仅仅是增量的数据,这时只能全量同步了。

20. 分布式锁

本地锁,可以用 JDK 实现,但是分布式锁就必须要用到分布式的组件。比如 ZooKeeper、Redis。网上代码一大段,面试一般也不要写,我这说一些关键点。

几个需要注意的地方如下。

  • 死锁问题:锁不能因为意外就变成死锁,所以要用 ZK 的临时节点,客户端连接失效了,锁就自动释放了。

  • 锁等待问题:锁有排队的需求,所以要 ZK 的顺序节点。

  • 锁管理问题:一个使用使用释放了锁,需要通知其他使用者,所以需要用到监听。

  • 监听的羊群效应:比如有 1000 个锁竞争者,锁释放了,1000 个竞争者就得到了通知,然后判断,最终序号最小的那个拿到了锁。其它 999 个竞争者重新注册监听。这就是羊群效应,出点事,就会惊动整个羊群。应该每个竞争者只监听自己前面的那个节点。比如 2 号释放了锁,那么只有 3 号得到了通知。

追问 1:watch 监听为什么是一次性的?

如果服务端变动频繁,而监听的客户端很多情况下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。

一般是客户端执行 getData(节点 A,true),如果节点 A 发生了变更或删除,客户端会得到它的 watch 事件,但是在之后节点 A 又发生了变更,而客户端又没有设置 watch 事件,就不再给客户端发送。

在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,我只要最新的数据即可。

追问 2:ZooKeeper 为什么不用数据库做持久化?

1 基础组件,就是基础,不能依赖太多。

追问 3:ZooKeeper 的 session 为什么由 server 维护,client 不行吗?

如果 session 由 client 管理,那么集群宕机了,然后恢复了,session 并不知道 client 失效了,临时节点也不会被清理,对 server 来说它依然是临时节点。

推荐阅读

Spring Boot 实现定时任务的 4 种方式

盘点阿里巴巴 15 款开发者工具

挑战10个最难回答的Java面试题(附答案)

让 Spring Boot 启动更快一点

    : . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存