vlambda博客
学习文章列表

腾讯面试之灵魂拷问 Zookeeper

2019年12月底时,我开启了财付通某开发岗的面试之路,这次的面试官非常严厉,一上来就问关于注册、服务中心与 Zookeeper 的相关问题,顿时让我措不及防。


什么是 CAP 定理与 BASE 理论

阅读


说说两阶段、三阶段流程

请阅读


谈谈对注册中心的了解

注册中心是微服务架构中非常重要的一个组件,在微服务架构里主要起到了协调者的一个作用,它一般包含如下几个功能:

  1. 服务发现:

    • 服务注册/反注册:保存服务提供者和服务调用者的信息。

    • 服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功能。

    • 服务路由:具有筛选整合服务提供者的能力。

  2. 服务配置:

    • 配置订阅:服务提供者和服务调用者订阅微服务相关的配置。

    • 配置下发:主动将配置推送给服务提供者和服务调用者。

  3. 服务健康检测

    • 检测服务提供者的健康情况。

以下是服务注册/发现的简述流程:

1、首先服务提供者启动,并向注册中心提供自身的IP、端口、接口名称等信息。

2、服务消费者启动时,向注册中心订阅服务提供者,并检查是否有可用的服务提供者,当注册中心注册的服务提供者发生变化时,下发服务变更通知消费者。


流行的服务中心有哪些

市面上有几款较流行、开源的注册中心,例如:ConsulZookeeper、Etcd、Nacos、Eureka、。

腾讯面试之灵魂拷问 Zookeeper

通过上面的比较,我们发现 除了Eureka、Nacos,其它的注册中心采取都是CP,牺牲了高可用性, 只有Eureka、Nacos是采用了AP,并且Nacos是同时支持CP。


注册中心采用 AP 还是 CP

从上面表格可知 ZK 采用的是 CP 模型,但注册中心最终选用 CP 还是AP,需根据业务场景决定,如果系统对数据一致性要求较高,且可容忍一定时间的不可用,则选用 CP 模型。

相反,如果可容忍一定时延的数据不一致性,但不能容忍不可用出现,则选用 AP 模型。


ZK 有哪几种节点类型

ZK 的节点类型有四种:

1、持久化目录节点。

2、持久化顺序编号目录节点,节点会根据当前已存在的节点编号自动加 1。

3、临时目录节点,客户端 Session 超时,节点就会被自动删除。

4、临时顺序编号节点,除临时目录节点特性外,节点根据已存在的节点编号自动加 1。


ZK 的使用情景有哪些

1、数据发布与订阅,即配置中心。发布者将数据发布到 ZK 节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。

2、分布式通知/协调。 ZK 中 watcher 注册与异步通知机制,能够很好的实现分布式环境下不同系统间的通知与协调,实现对数据变更的实时处理,通常是不同的系统对 ZK 上同一个 znode 进行注册,并监听变化。

3、集群管理与 Master 选举。通过 ZK 的 wathcer 机制与临时性节点的特性,可动态的监控集群中的每个节点状态。而 Master 选举则是 ZK 中最为经典的应用场景了,当集群中的节点同时发起选举时,利用临时顺序类型节点的特性,每次选取序列号最小的机器作为Master 。

4、分布式锁。根据 ZK 的强一致性,保证在分布式高并发情况下节点创建的全局唯一性,最终只有一个客户端请求能够创建成功,即表示获取锁成功,同时可以控制时序,保证所有获取这个锁的客户端,都会被安排执行。

5、分布式队列。利用分布式锁服务中的控制时序,可保证请求客户端的顺序执行。


ZK 是如何保证数据一致性

ZAB(Zookeeper Atomic Broadcast)协议是为 Zookeeper 专门设计的一种支持崩溃恢复的原子广播协议。

Zookeeper 主要依赖 ZAB 协议来实现分布式数据一致性,Zookeeper 基于该协议实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。

ZAB协议包含两种基本模式,分别是:

1、崩溃恢复之数据恢复

当整个 Zookeeper 集群正在启动,或者当 Leader 节点出现网络中断、崩溃等情况时,ZAB 协议就会进入恢复模式并选举产生新的Leader,当 Leader 服务器选举出来后,并且集群中有过半的机器和该 Leader 节点完成数据同步后(同步指的是数据同步,用来保证集群中过半的机器能够和 Leader 服务器的数据状态保持一致),ZAB 协议就会退出恢复模式。

2、消息广播之原子广播

当集群中已经有过半的 Follower 节点完成了和 Leader 状态同步以后,那么整个集群就进入了消息广播模式。在 Leader 节点正常工作时,启动一台新的服务器加入到集群,那这个服务器会直接进入数据恢复模式,和 Leader 节点进行数据同步,同步完成后即可正常对外提供非事务请求的处理。


ZK 是如何保证事务顺序一致性

ZK 采用了递增的事务 Id 标识,所有的 proposal 提议都在被提出时加上了 zxid,zxid 实际上是一个 64 位的数字,高 32 位 表示 epoch 周期,用来标识 Leader 关系是否发生变化,每次 Leader 被选出来,都会生成一个新的 epoch(原来的epoch+1),而低 32 位是一个递增计数。

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

当新产生 proposal 提议的时候,会依据两阶段提交过程,首先会向其他的节点发出事务预执行请求,当超过半数的节点反馈能执行时,那么其他节点就会真正执行。


为什么建议 ZK 节点数为奇数 ?

首先 Leader 选举,要求 可用节点数量 > 总节点数量/2 ,注意:是 > 而不是 >=。

1、防止由脑裂造成的集群不可用。

脑裂,发生在节点之间通信不可达的情况下,集群会分裂成不同的小集群,小集群各自选出 master 节点,最后导致原有的集群出现多个 master 节点的情况。

1、Zookeeper 集群有五个节点,发生脑裂后分别出现两个小集群 A,B,有两种情况:

A 集群:1个节点,B 集群:4个节点

A 集群:2个节点,B 集群:3个节点

在上面两种情况下,A、B 总有一个小集群满足 可用节点数量 > 总节点数量/2,Zookeeper 仍能完成 Leader 选择,并对外提供服务,只是另外一个小集群失效了。

2、假如 Zookeeper 集群只有四个节点,发生脑裂后分别出现两个小集群 A,B:

A 集群:1个节点,B 集群:3个节点

A 集群:2个节点,B 集群:2个节点

同样可发现第一种情况也可满足条件、完成 Leader 选择,但第二种情况都是两个节点而不满足选举条件,因此 Zookeeper 彻底不能提供服务。

因此,在节点数量是奇数个的情况下, Zookeeper 集群总能对外提供服务,只是损失了部分节点,如果节点数量是偶数个,会存在zookeeper集群不能用的可能性(脑裂成两个均等的子集群的时候)。

2、在容错能力相同的情况下,奇数节点数更节省资源。

当 Zookeeper 集群有 3 个节点,如果需要正常提供服务,就需保证有 2 个节点正常( 3/2 = 1.5 ),允许 1 个节点故障。

当 Zookeeper 集群有 4 个节点,如果需要正常提供服务,就需保证有 3 个节点正常( 4/2 = 2 ),同样只允许 1 个节点故障。

在相同容错能力下,本着节约资源的原则,Zookeeper 集群的节点数维持奇数更好。


讲讲 ZK 的 watch 机制

Zookeeper 的 watch 监听机制,是用于监听节点数据变化,当监听的节点数据发生变化时,会通知该 watch 的客户端,它有以下特性:

1、watch 监听是一次性的。每次都需要重新注册,并且客户端在会话异常结束时不会收到任何通知,而快速重连时仍不影响接收通知。

2、watch 的回调执行是顺序的。客户端在没收到数据的事件之前是看不到最新的数据,另外注意不要在 watch 回调逻辑中阻塞流程。

3、watch 是轻量级的。WatchEvent 是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,ZooKeeper 服务端只会通知客户端发生了什么,并不会告诉具体内容。


注册服务太多时, ZK 会出现什么问题

1、注册服务量多,引发超时,从而导致重新选举。随着注册的服务数量增多,Zookeeper 的 Leader 和 Follower 节点间同步的数据量逐渐变多,同时鉴于数据量大,可能出现超时从而引起重新选举。

2、数据同步期间无法对外提供服务。鉴于 Zookeeper 的强一致性,在节点同步数据期间将无法对外提供服务,客户只能一直等待节点完成同步。

解决方案:

  • 增大 Zookeeper所在虚拟机的堆栈内存

  • 增加 tickTime 或者 initLimit 和 syncLimit 的值,或两者都增大。

  • 增大最大客户端连接数。

initLimi t: Follower在启动过程中,会从 Leader 同步所有最新数据,然后确定自己能够对外服务的起始状态, Leader 允许 Follower 在 initLimit 时间内完成这个工作,默认是: 10 * ticktime。


tickTime:ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如:session的最小超时时间是2 * tickTime。

syncLimit:在运行过程中,Leader 负责与 ZK 集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态,默认是:5 * ticktime 。


如何从设计上解决注册服务多引发的问题

数据分片:把所有注册服务的数据按照一定维度进行拆分,并存储到不同节点,同时为了尽可能地保证数据存储的均匀分布,理论上采用一致性 Hash 算法对数据进行分片,集群可以根据数据量进行无限地扩展,详细可参考蚂蚁金服注册中心 SOFARegistry  的设计。


如何避免服务跨机房、跨城调用

在注册服务时,服务提供者把自身 ip、port、机房、区域位置等信息携带到注册中心当消费者订阅服务时,向注册中心拉取可用的服务提供者信息。

在服务消费者向提供者发起调用时,根据一定的路由算法,优先调用同机房、同城的服务。

当无发现同机房、同城服务时,再根据区域位置信息计算最近区域的的服务提供者。


服务容灾采用什么方案

容灾系统是指在相隔较远的异地,建立两套或多套功能相同的 IT 系统,互相之间可以进行健康状态监视和功能切换,当一处系统因意外(如火灾、地震等)停止工作时,整个应用系统可以切换到另一处,使得该系统功能可以继续正常工作。

容灾技术是系统的高可用性技术的一个组成部分,容灾系统更加强调处理外界环境对系统的影响,特别是灾难性事件对整个 IT 节点的影响,提供节点级别的系统恢复功能。

常用的容灾部署方案有:同城双活、异地多活、两地三中心、三地五中心,而当时自己所在公司采用的是两地三中心方案

两地三中心容灾方案,两地三中心,两地是指同城、异地,三中心是指生产中心、同城容灾中心、异地容灾中心。

本地机房的容灾主要是用于防范生产服务器发生的故障,而异地灾备中心用于防范大规模区域性灾难。

本地机房的容灾与生产中心处于同一个机房,可通过局域网进行连接,因此数据复制和应用切换比较容易实现,可实现生产与灾备服务器之间数据的实时复制和应用的快速切换。

异地灾备中心由于其与生产中心不在同一机房, 灾备中心与生产中心连接的网络线路带宽和质量存在一定的限制,应用系统的切换也需要一定的时间。
因此异地灾备中心可以实现在业务限定的时间内进行恢复和可容忍丢失范围内的数据恢复。