第一章 Zookeeper理论基础
1.1 什么Zookeeper?
开源的分布式应用程序协调服务器,为分布式系统提供一致性服务。通过Paxos算法和ZAB协议完成,主要功能包括:配置维护、域名服务、分布式同步、集群管理
1.2 一致性
1.2.1 顺序一致性
同一个客户端发起的多个事务请求,最终会严格按照其发起顺序记录到zk中
1.2.2 原子性
所有请求在ZK集群中的每个节点上是一致的,要么全部节点都是成功,要么全部都是失败
1.2.3 单一视图
客户端连接的集群中任意一节点,读取的数据都是一致的
1.2.4 可靠性
一旦事务被成功应用到zk,会一直保留下来,除非另一个事务将其修改
1.2.5 最终一致性
一旦一个事务被成功应用,zk可以保证在较短时间内,客户端最终一定能从服务端读取到最新的数据,但不保证实时读取到
1.3 Paxos算法
prepare阶段:提案者发送提案的编号为N给所有表决者,表决者用自己保存的最大接收编号maxN比较,只有N > maxN才同意,表决者缓存maxN = N,并发送自己历史最大接收提案 Proposal(myid,maxN,value)
accept阶段:当半数表决者同意,提案者发送正真提案,Proposal(myid,N,value),表决者收到后,再次用maxN比较,只有N >= maxN,才同意,并更新Proposal(myid,N,value)。
prepare和accept都必须要半数以上表决者同意,否则提案者只能重新进入prepare阶段
1.3.1 活锁问题
(图引用:https://blog.csdn.net/wolf_love666/article/details/92832811)
解决办法,只允许一个进程提交提案,即对N的增长有唯一操作权限
1.4 ZAB协议
zookeeper 原子广播协议。当客户端连接到zk集群的一个节点后,如果客户端提交的读请求,节点直接响应。如果提交的是写请求,且当前节点不是Leader,会将请求转发给Leader,Leader以提案的方式广播该写操作,只有半数以上的节点同意写操作,写操作才会被提交,之后Leader会再次广播给所有订阅者,即Learner,通知它们同步数据。
1.4.1 三类角色
- Leader:事务请求的唯一处理者,也可以处理读请求
- Follower:处理客户端读请求,将事务请求转发给Leader,对Leader的提案有表决权,同时可以参加Leader的选举,有选举权和被选举权
- Observer:不参与选举,没有选举权和被选举权。可以协助Follower处理读请求,用来提高集群读请求吞吐量,同时不会增加选举压力
再分类:
- Learner:需要从Leader中同步数据的节点,Follower+Observer
- QuorumServer:能参与选举的节点,Leader+Follower
1.4.2 三个数据
- zxid:Long型数字,高32位代表epoch,低32位表示xid
- epoch:每个Leader选举结束会生成一个新的epoch,通知到集群中其他节点,包含Follower和Observer
- xid:事务ID
1.4.3 三种模式
- 恢复模式:Leader崩溃或集群启动过程中,系统进入恢复模式,包含Leader选举和初始化同步
- 广播模式:初始化广播和更新广播
- 同步模式:初始化同步和更新同步。初始化广播触发Learner进行初始化同步,将初始化广播中的事务同步到本地。类似更新同步由更新广播触发,将正常通信阶段的事务同步到本地。
1.4.4 四种状态
集群中每个节点可能的状态
- LOOKING:选举状态
- FOLLOWING:Follower的正常工作状态
- OBSERVING:Observer的正常工作状态
- LEADING:Leader的正常工作状态
1.4.5 同步模式和广播模式
1. 初始化广播
选举完毕后只是准Leader,需要经过初始化同步后,才能变成真正的Leader。这期间会将Leader本身有而其他Learner没有的事务广播出去,并且探测一下活跃的Learner。
- 为每个Learner创建一个FIFO队列
- 将新Leader中未同步的事务封装成Proposal
- 将Proposal逐条发送给各个Learner,并紧接着附加一个COMMIT消息
- Learner收到消息,更新到本地,成功后返回ACK
- Leader收到ACK后,将对应Learner加入可用的Follower或者Observer列表
2. 消息广播算法
当初始化完毕后,进入正常工作模式,当非Leader收到事务请求,将转发给Leader
- Leader封装事务为Proposal,并生成一个全局唯一的zxid
- 从Follower列表获取所有Follower,将Proposal通过各自的FIFO队列发送给对方
- Follower收到后,对比自身存储的最大zxid。大于最大zxid,存储到本地事务日志,返回Leader ACK
- 当Leader收到过半ACK,向所有Follower队列发送COMMIT消息,向所有Observer队列发送Proposal
- Follower收到后,会将日志中的事务正式更新到本地。Observer收到后,直接应用事务到本地。两者处理完后都需要发送ACK
3. Observer的数量问题
一般与Follower数量相同,Observer需要从Leader同步数据,但Observer同步时间小于等于Follower的同步时间,一旦Follower同步完成,那么Observer同步也将结束,这就导致可能部分尚未完成同步,那么这些未同步完成的Observer将不能对外提供服务,造成浪费
1.4.6 恢复模式的三个原则
1. Leader的主动让出原则
当Leader收到Follower的心跳数量没有过半,此时Leader认为自己与集群的连接出现问题,会主动修改自己的状态为LOOKING,寻找新的Leader,防止出现脑裂。其他Server有过半主机发现Leader丢失,将会选举新的Leader
2. 已经处理过的消息不能丢弃原则
当Leader发送的COMMIT消息未完全被learner接收,Leader宕机,当新Leader选举后,需要根据这个来恢复执行
3. 被丢弃的消息不能再现原则
Leader在prepare阶段通过的事务,在发送COMMIT阶段之前宕机,再次上线后,需要丢弃这个事务
1.4.7 Leader选举
1. myid:也称为ServerId,是zk集群中服务器的唯一标识
2. 逻辑时钟:Logicalclock,选举完毕,变成epoch
一、集群启动中的Leader选举
只有所有节点都处在LOOKING状态才能进行选举。每个服务器启动后,状态为LOOKING,会给自己投票(myid,zxid),将投票发送给集群所有服务器。投票的比较规则:
- 优先检查zxid,较大的优先作为Leader
- 若相同,比较myid,较大的作为Leader
每个服务器会选出优胜票更新投票,直到有半数服务器收到相同的投票信息,优胜的更新状态为LEADING,失败的更新为FOLLOWING
若后续再有服务器启动,发现其余节点不是LOOKIGN,自动更新状态为FOLLOWING
二、宕机后的Leader选举
首先更新状态为LOOKING,然后发送投票,每个服务器依然投给自己(myid,zxid),注意此时zxid可不是启动时的0。后续步骤一致
1.5 高可用集群的容灾
1.5.1 服务器数量的奇数和偶数
无论写操作投票还是Leader选举的投票都需要半数以上节点同意,所以当半数以上节点宕机,则投票永远无法通过。如5台服务器只允许2台宕机,而6台也只允许2台宕机,所以5台和6台的容灾能力是一样的,基于容灾考虑,建议部署5台服务器避免浪费,但基于吞吐量,6台服务器更好
1.5.2 容灾设计方案
多机房部署设计,要充分考虑过半原则。比如三机房部署,每个机房中的主机数量要少于集群总数的一半,这样一个机房断网或断电,集群仍可以对外提供服务。双机房部署,需要个机房过半
1.6 ZK和CAP
zk遵循的是CP原则,牺牲了可用性。
1. 当Leader宕机后,zk集群在选举时,不提供读写服务。
2. 当过半下线后,集群也无法提供服务。
1.7 ZK可能出现脑裂
多机房部署,当出现网络连接,形成多个分区,可能出现脑裂,导致数据不一致。