Zookeeper¶
简单介绍一下zookeeper¶
Zookeeper是一个开源的分布式的用于为分布式应用提供协调服务的Apache项目(主要基于文件系统+通知机制)
※为什么要用zk?¶
- 统一命名服务:在分布式环境下,经常需要对服务进行统一命名,便于识别,例如ip地址
- 统一配置管理:在一个集群中,要求所有节点的配置信息是一致的
- 统一集群管理:在一个集群中,需要实时监控每个节点的状态变化
- 负载均衡:在zookeeper中记录每台服务器的访问数,再次请求的时候,让访问最少的服务器去处理当前请求
※zk的数据持久化是怎么做的¶
- zk中的数据是保存在节点上的,节点就是znode,多个znode之间构成一棵树的目录结构zk的数据是运行在内存中,zk提供了两种持久化机制
- 事务日志(保存执行命令):zk把执行的命令以日志形式保存在dataLogDir指定的路径中的文件中(如果没有指定dataLogDir,则按照 dataDir指定的路径)
- 数据快照(保存机器状态):zk会在一定的时间间隔内做一次内存数据快照,把当前时刻的内存数据保存在快照文件中
- zk通过两种形式的持久化,在恢复时先恢复快照文件中的数据到内存中,再用日志文件中的数据做增量恢复,这样恢复的速度更快
※※※zk的选举机制¶
-
zookeeper的选举原则
-
zxid(事务ID)优先原则
- 若节点有历史数据(
zxid > 0),则zxid最大的节点直接成为Leader(无论myid大小)。 - 若所有节点的
zxid相同(如全新集群),则进入myid比较阶段。 - 半数投票原则
- Leader必须获得超过半数节点的投票(
n/2 +1)才能正式成为Leader。
zookeeper第一次启动:myid最大的节点会优先成为Leader,且遵循半数投票原则
- 假设有3台服务器,服务器1先启动,此时只有它一台服务器启动了,没有任何服务器可以进行通信,因此处于Looking状态
- 紧接着服务器2启动,它就会和1进行通信,交换选举结果,此时id较大的2胜出
- 此时也满足半数以上的服务器同意选举2,所以2就成为了leader
- 最后服务器3启动,虽然自己的id大一些,但是前面已经选出了leader,因此自己就成为了follower
- 如果2号机挂机,则按照前面逻辑处理,3号机就是leader,即便2号机恢复也只能是成为follower
zookeeper启动的时候存在旧数据:先比较zxid,当zxid相同时,才比较myid并进行半数投票
- 假设服务器3曾处理过事务,
zxid=100,服务器1、2为全新节点,zxid=0 - 服务器1启动,进入Looking状态
- 服务器2启动,发现服务器1的
zxid=0,开始比较myid(2 > 1),服务器2暂时成为Leader - 服务器3启动,广播
zxid=100 - 服务器1、2发现服务器3的
zxid更大,立即放弃竞争,投票给服务器3 - 服务器3获得3票(超过半数),成为Leader
※zookeeper有三种角色之间的功能与区别¶
zookeeper中有三种角色:老大Leader(领导者) 、老二Follower (跟随者) 、老三Observer(观察者),其中,Follower和Observer归类为Learner(学习者),按重要性排序是Leader > Follower > Observer
- Leader
Leader在集群中只有一个节点,是集群中心,负责协调集群中其他节点。从性能的角度考虑,leader可以选择不接受客户端的连接。
- 发起与提交写请求。所有的跟随者Follower与观察者Observer节点的写请求都会转交给领导者Leader执行。Leader接受到一个写请求后,首先会发送给所有的Follower,统计Follower写入成功的数量。当有超过半数的Follower写入成功后,Leader就会认为这个写请求提交成功,通知所有的Follower commit这个写操作,保证事后哪怕是集群崩溃恢复或者重启,这个写操作也不会丢失。
- 与learner保持心跳
-
崩溃恢复时负责恢复数据以及同步数据到Learner(以Follower为主)
-
Follower
Follower在集群中有多个
- 与Leader保持心跳连接
- 当Leader挂了的时候,经过投票后成为新的leader
Leader的重新选举是由Follower们内部投票决定的。
- 向leader发送消息与请求
-
处理leader发来的消息与请求
-
Observer
Observer是zookeeper集群中最边缘的存在
- 主要作用是提高zookeeper集群的读性能。由于zookeeper的一个写操作是要经过半数以上的Follower确认才能够写成功的。那么当zookeeper集群中的节点越多时,zookeeper的写性能就越差。而Observer角色可以实现在提高zookeeper读性能(也就是支持更多的客户端连接)的同时又不影响zookeeper的写性能
- 与Leader同步数据
- 不参与leader选举,也不参与写操作的提议过程
- 数据不持久化到硬盘,只将数据加载到内存
※※※※zab协议和raft协议的区别以及zookeeper基于什么协议¶
- zookeeper基于zab协议
| Raft协议 | Zab协议 | |
|---|---|---|
| 一致性模型 | Leader-Follower模型 | Leader-Follower模型 |
| 一致性强度 | Raft协议保证一旦大多数Follower确认,日志条目就被标记为已提交,确保了强一致性 | Zab协议在提交事务时,只需要得到Quorum的确认,因此保证的是弱一致性 |
| 选举条件 | 在Raft中,如果 Follower 在一段时间内没有收到 Leader 的心跳信息,就会主动进入候选人状态并启动新一轮选举,原因可能只是 Leader 节点繁忙而无法及时响应等情况 | 在Zab中除了系统启动时,只有等到 Leader 故障,无法提供服务时才会进行选举 |
| Raft更偏向于积极选举,只要有可能就尝试选举新的领导者,而ZAB更倾向于保持现状,只有在确定领导者无法提供服务时才触发选举 | ||
| 投票规则 | Raft 中,每个节点只能投票一次,并且投票给第一个请求投票的候选人 | Zab中,节点可能会改变自己的投票,投给 zxid 更大的节点。所以 Zab 不存在随机超时时间,虽然第一票都会投给自己,但后续如果决定其他节点的 zxid 更大,就修改自己的选票 |
| 票数要求 | 在Raft 中,候选人在获得超过半数的投票后成为 Leader | 在 Zab 中,新的领导者被选出后,还需要得到超过半数的节点的确认才能开始工作 |
| 决定因素 | 在Raft中,选举过程严格按照轮次进行,每轮选举只能选出一位Leader,选出 Leader 很大程度取决于随机的超时时间和日志最新 | 在 Zab 中,选举的结果主要取决于节点的 zxid 以及节点编号。zxid最大的节点通常是最近处理过事务,数据最新的节点 |
| 日志复制机制 | Raft协议将每个节点的日志直接复制到其他节点,降低了中心化的压力 | Zab协议将每个节点的事务日志存储在Leader节点中,然后通过Leader节点进行复制 |
| 可扩展性 | Raft协议可以通过水平扩展来增加整个系统的容量和性能 | Zab协议的可扩展性受限于Leader节点的性能 |
※paxos 算法¶
paxos 算法是一种基于消息传递且具有高度容错特性的一致性算法,用来解决分布式系统的数据一致性的问题
paxos系统中将所有节点划分为proposer(提议者),acceptor(接收者),learner(学习者)
paxos 算法主要分为三个阶段:
- prepare 准备阶段:首先 proposer 向多个 acceptor 发出 propose 请求 (proposer 生成全局唯一且递增的
proposal id,没有携带提案内容),然后acceptor 针对收到的请求进行promise承诺 - accept 接收阶段:proposer收到过半数acceptor 的promise 承诺后, 再向acceptor 发出propose 请求,然后acceptor 针对收到的请求进行accept 处理
- learn学习阶段:proposer将通过的决议发送给所有learner(服从proposer)
※※※Zab协议¶

zab协议借鉴了paxos算法,是专门为zookeeper设计的支持崩溃恢复的原子广播协议。
- 消息广播
paxos算法中采用多个提案者会存在竞争acceptor的问题,于是zab协议就只采用了一个提案者。zookeeper 的一致性就是基于 zab 协议实现的,即只允许一个leader作为proposer发起提案。
它包括两种基本的工作模式:正常和异常
-
正常模式
-
首先客户端Client向leader 发送写操作请求
- 紧接着 leader 将客户端的请求转换为事务proposal 提案,同时为每个proposal分配一个全局唯一的zxid
- 然后leader 会为每个follower(作为Accepter)分配一个单独的队列,将需要广播的 proposal 依次放到队列中,并且根据先进先出的策略向follower发送提案
- 【两阶段提交:ACK】当follower接收到proposal提案之后,会首先写入本地磁盘中, 写入成功后向leader响应ack
- 【两阶段提交:Commit】当leader接收到半数以上(Quorum)的服务器的ack响应之后,即认为提案发送成功,可以发送commit消息
-
【两阶段提交:Commit】然后leader就会向所有follower广播 commit 消息,同时自身也会进行事务提交,所有follower 接收到后,就会进行事务提交
-
崩溃恢复模式
-
leader发起事务提案之后就宕机了,此时follower还没有收到提案
- leader 收到半数的ack以后,还没来得及发送commit消息就宕机了
那么这时候就涉及到leader选举和数据恢复两个过程
-
leader选举
-
所有节点(除了Observer不参与投票)均可作为Proposer发起选举提案,同时也作为Accpeter收集其他节点的提案并进行投票,选举出
zxid最大或myid最大的节点为Leader。 - 新的leader必须满足两个条件
- 第一,新的leader必须是已经提交了 proposal 的 follower 节点
-
第二,新的leader 节点含有最大的zxid(其次才是最大的myid)
-
数据恢复
在新的leader选举之后,在正式工作开始之前,leader会确认所有的 proposal 是否已经被集群中过半的服务器提交,同时等到follower将所有尚未同步的提案都从 leader 上同步过,并且应用到内存数据中以后,leader 才会把该follower 加入到真正可用的follower列表中。
※※简述什么是 CAP 理论,zookeeper 满足 CAP的哪两个¶
C表示Consitency(一致性,也就是从每个节点读取的数据是一样的),A表示Avaliablity(可用性,也就是整个系统一直处于可用的状态),P表示Partition tolerance(分区容错性,分布式系统在任何网络分区故障问题的时候,仍然能正常工作)
对于一个分布式系统来说的话,最多只能满足其中的两项,并且满足P是必须的,所以往往选择就在CP或者AP中。而zookeeper就是满足了一致性和分区容错性(CP),因为leader节点挂掉的时候,集群会重新选举出leader,在这个期间集群是不满足可用性的
- 为什么只能满足其中的两项?
比如说两个机器之间的数据同步出现了问题,如果想要保证可用性,那么不管数据的一致性,继续提供服务就可以了;如果想要保证一致性,那么就需要等待一段时间进行数据恢复,在 这个期间,集群是不满足可用性的 ,所以一个分布式系统要么满足AP,要么满足CP 。
※※zookeeper 集群的节点数为什么建议奇数台 (zk机房扩容有什么要注意的吗)¶
zookeeper集群的节点数量应为奇数:因为根据paxos理论,只有集群中超过半数的节点还存活才能保证集群的一致性。
- 因为zookeeper 中有一个半数可用机制。集群中只要有半数以上的 机器正常工作,那么整个集群对外就是可用的。比如说如果有2个zookeeper, 那么只要1个死了zookeeper就不能用了,因为1没有过半,那么zookeeper 的死亡容忍度为0,同理,如果有3个zookeeper,如果死了1个,还剩2个 正常,还是过半的,所以zookeeper 的死亡容忍度为 1,而2n和2n-1的容忍度是一样 的,所以为了节约资源,就选择奇数台
- 防止因为集群脑裂造成集群用不了。比如有4个节点,脑裂为2个小集群, 都为2个节点,这时候,不能满足半数以上的机器正常工作,因此集群就不可用了,那么当有5个节点的时候,脑裂为2个小集群,分别为2和3,这 时候3这个小集群仍然可以选举出leader,因此集群还是可用的