深入分析zookeeper实现原理

 zookeeper 的设计猜想

zookeeper 主要是解决分布式环境下的服务协调问题而产生的,如果我们要去实现一个 zookeeper 这样的中间件,我们需要做什么?

1.防止单点故障

如果要防止单点故障,那就势必要做zookeeper集群。而且如果还需要满足相应的性能要求的话,那就得是一个高性能高可用的集群。高性能意味着这个集群里的机器能够共同分担客户端的流量,高可用意味着集群中的任意节点宕机后,不会影响整个集群的数据并且集群是还可以继续正常对外提供服务的。

所以:这个中间件需要考虑到集群,而且这个集群还要分担客户端的请求流量。

2.集群之间的数据同步

接着上面的结论继续思考,如果要满足这样的一个高性能集群,每个节点肯定要分担请求流量,那么每个节点的数据就必须保持一致。要实现各个节点的数据一致性,就必须要一个leader节点负责协调节点间的数据同步。如果在这样一个集群中没有leader节点那么每个节点都可以接收所有的读写请求,那么就增加了数据同步问题的复杂度。

所以:这个集群中涉及到数据同步需要一个leader节点。

3.leader选举

继续思考,如何在这些节点中选举出leader,以及集群中的leader挂了之后该怎么办,如何选举出新的leader继续提供服务?

所以:zookeeper基于paxos理论设计出了ZAB协议。

4.怎么解决分布式事务

在分布是系统中,每一个机器虽然都明确的知道自己的事务操作是成功还是失败,但是却无法直接获取其他节点的数据执行结果。所以当一个事务操作涉及到跨节点的时候,就需要用到分布式事务,分布式事务的数据一致性协议有2PC 和3PC协议。

结论:基于这些猜想,我们基本知道了zookeeper为什么要用ZAB理论来做leader选举,为什么要做集群,为什么要用到分布式事务来实现数据的一致性,接下来就逐步分析下这些实现内容。

zookeeper 的集群

在zookeeper中,客户端会随机连接到zk集群中的某个节点,如果是读请求,就直接从当前节点总读取数据;如果是写请求,那么久会被转发给leader节点,然后leader节点会向集群中的节点广播事务,只要有半数的节点写入成功,那么写请求就会被提交。

 

所有事务请求都必须由集群中唯一的服务器(协调者)来处理,这个服务器就是leader服务器,而参与者就是其他的follower服务器。leader服务器把客户端的写请求转化为一个事务提议(Proposal),并把这个提议分发给集群中的所有follow服务器(不是所有服务器),之后leader服务器需要等待所有follower服务器的反馈,一旦超过半数的follower节点同意这个提议,那么leader节点就会再次向集群中的所有follower节点发送commit消息,要求各个follower节点对前面的事务进行提交。

zk集群中角色

Leader 角色

leader节点是整个zookeeper集群的核心,主要的职责有两个:

1.事务请求的唯一调度和处理者,确保集群事务处理的有序性

2.集群内各个服务器的调度者

Follower 角色

follower角色的主要职责是:

1.处理客户端的非事务请求,转发事务请求到leader节点

2.参与leader事务请求提议的投票(需要集群中半数以上节点同意leader才会发起提交请求)

3.崩溃恢复时参与leader的选举投票

Observer 角色

observer是zookeeper3.3才开始引入的节点角色。它的作用是观察集群中节点的状态变化,并且同步到observer节点。observer的工作原理与follower节点基本一样,唯一的不同是observer角色不参与集群中任何形式的投票,包括事务请求提议和leader选举的投票。observer节点只提供非事务请求的服务,这样设计的目的是在不影响集群事务处理能力的前提下提升集群中非事务请求的处理能力。

集群组成

通常zookeeper是由2n+1台server组成,每个server都知道彼此的存在。对于2n+1台server,只要有n+1台server可用,那么整个系统就能正常对外提供服务。之所以要满足这样一个等式,是因为一个节点要成为集群中的 leader,需要有超过及群众过半数的节点支持,这个涉及到 leader 选举算法,同时也涉及到事务请求的提交投票。基于这个特性,如果想搭建一个能够允许N台机器挂掉的集群,那么就需要部署2N+1台服务器来构成集群。因此,3台机器构成的集群能够在挂掉1台的情况下依然能对外提供服务;5台机器构成的集群能够在2台机器不可用的情况下进行容灾。如果搭建6台机器构成的集群,和5台式一样的效果,都是只允许2台挂掉,所以集群机器个数一般都是单数。系统启动时,集群中的节点会通过选举算法选出一台机器作为leader节点,其他的就是follower。

ZAB 协议

zab协议(zookeeper atomic broadcast)是zookeeper专门设计的一种支持崩溃恢复的原子广播协议。在zookeeper集群中,主要依赖该协议来实现分布式数据一致性,基于该协议,zookeeper实现了主备模式的系统架构来保持集群中各个副本之间数据的一致性。

zab协议介绍

    zab协议包括两种基本模式,分别是:1.崩溃恢复 2.原子广播。

    当集群在启动的过程中或者leader节点出现网络中断,网络崩溃等情况时ZAB协议就会进入恢复模式并且选举产生新的leader,当服务器的leader节点选举出来后,并且集群中有过半机器和leader节点同步完数据后,ZAB协议就会退出恢复模式。当集群中已经有过半的follower节点完成了和leader节点的状态同步后,那么整个集群就进入了消息广播模式。这个时候,在leader节点正常工作时,启动一台新的服务器加入集群,那么这个服务器就进入了数据恢复模式,和leader节点进行数据同步,等同步完成后才可以对外提供非事务请求的服务。

消息广播实现的原理

消息广播的过程实际上是一个简化版的二阶提交(2pc):

1.leader接收到请求消息后,将消息赋予一个全局唯一的ZXID,通过ZXID的大小比较即可实现事务有序性这个特征。

2.leader为每个follower准备了一个FIFO队列,将带有ZXID的消息作为一个proposal(提议)分发给所有的follower。

3.当follower接收到proposal后,先把proposal写入磁盘,写入成功后再回复一个ack给到leader。

4.当leader接收到合法数量的ack后,leader就会向这些follower发送commit命令,同时也会再自己本地执行该消息。

5.当follower收到commit消息后,会在本地提交事务。

崩溃恢复(数据恢复)

ZAB 协议的这个基于原子广播协议的消息广播过程,在正常情况下是没有任何问题的,但是一旦leader节点崩了,或者由于网络问题导致leader和follower节点失去了联系(可能产生了网络分区),那么此时的leader就不再是一个合法的leader了,接下来就会进入崩溃恢复模式。在ZAB协议中,为了保证集群的正确运行,整个恢复过程结束后需要选举出一个新的leader。为了使leader挂了后系统能够正常工作,需要解决以下两个问题:

1.已经被处理的消息不能丢失

当leader收到合法数量的ack后就向各个follower广播commit命令,同时也会在本地执行提交命令并向客户端返回成功。但是如果在各个follower收到commit命令前leader就挂了,导致剩下的节点并没有都执行这个消息。

leader对事务发起commit操作,但是该消息在follower1上执行了,但是在follower2上还没收到commit就已经挂了,而实际上客户端已经收到该事务消息处理成功的回执了。所以在ZAB协议下需要保证所有机器都执行这个事务消息。

2.被丢弃的消息不能再次出现

当leader接收到消息请求生成proposal后就挂了,其他follower并没有收到此proposal,因此经过恢复模式重新选举leader后,这个消息是被跳过的。此时,之前挂了的leader再次重新启动加入集群后注册成了follower,他保留了被跳过消息的proposal状态。与整个系统的状态是不一致的,需要将其删除。

    ZAB协议需要满足上面两种情况,就必须要设计一个leader选举算法,能够保证已经被提交的事务proposal不能丢失,同事丢弃已经被跳过的事务proposal。

   针对这个要求,如果leader选举算法能够保证新选举出来的leader节点拥有集群中最大ZXID的事务proposal,那么就可以保证这个新选举出来的leader一定具有已提交的提案,因为所有提案被commit之前必须有过半的follower ACK,即必须超过半数节点的服务器的事务日志上有该提案的proposal,因此只要有合法数量的正常节点工作,就必然有一个节点保存了所有被commit消息的proposal状态。

关于 ZXID

zxid也就是事务id。为了保证事务的顺序一致性,zookeeper采用了递增的事务id来标识事务。所有的提议在被提出的时候加上了zxid。

zxid是64位,高32位是epoch编号(ZAB协议通过epoch来区分leader周期变化的策略),用来标识leader关系是否改变,每经过一次选举就会产生一个新的leader,同时epoch编号也会加1,低32位是消息计数器,每接收到一条消息这个值就会加1,当新的leader选举出来后这个值就会重置为0。这样设计的好处在于当旧的leader重新加入集群后,它不会被选举为leader,因此它的zxid肯定小于新的leader。当老的leader作为follower加入集群后,新的leader会让它将所有旧epoch下未被提交的proposal清除。

leader 选举

集群启动的时候会leader选举

每个节点启动的时候都是locking(观望状态),接下去就是进行leader选举,假设搭建的集群是由3台机器构成的,在集群初始化阶段,当第一台服务器server1启动时,他是无法完成leader选举的,当第二台机器server2启动后,两台机器就开始就开始进行通信,每台机器都尝试找到leader,于是就进入了leader选举过程:

(1)每个server发出一个投票:

每次投票都会包含所需要推举服务器的myid和zxid,使用(myid,zxid)来表示;第一次投票大家都会把票数投给自己,比如server1的投票为(1,0),server2的投票为(2,0),然后将各自的投票结果发送到集群中的其他机器上。

(2)接受来自各个服务器的投票:

集群中的每个服务器在收到票数后,首先判断该投票的有效性,比如检查epoch以及是否是来自该集群的投票。

(3)处理投票:

针对每个投票,服务器都需要将别人的投票和自己的进行比较,比较的规则如下:

1.优先检查zxid,zxid比较大的服务器有限作为leader

2.如果zxid相同,那么就比较myid,myid比较大的服务器作为leader

(4)统计投票:

每次投票后,服务器都会统计投票信息,判断是否已经有过半的机器接受到了相同的投票信息。

(5)改变服务器状态:

一旦确定了leader,每个服务器就会更新自己的状态,如果是follower那么就会变成following状态,如果是leader,就变成leading。

运行过程中的leader选举

当集群中的leader节点出现宕机或者不可用的情况,那么整个集群将无法对外提供服务,而是进入新一轮的leader选举,集群运行期间的leader选举过程和启动时的选举过程一致。

(1) 变更状态。Leader 挂后,余下的非 Observer 服务器都会将自己的服务器状态变更为 LOOKING,然后开始进入 Leader 选程。

(2) 每个Server会发出一个投票。在运行期间,每个服务器上的 ZXID可能不同,此时假定 Server1 的 ZXID 为123,Server3的ZXID为122;在第一轮投票中,Server1和 Server3都会投自己,产生投票(1, 123),(3, 122),然后各自将投票发送给集群中所有机器。接收来自各个服务器的投票。与启动时过程相同。

(3) 处理投票。与启动时过程相同,此时,Server1 将会成为 Leader。
(4) 统计投票。与启动时过程相同。
(5) 改变服务器的状态。与启动时过程相同。

2PC提交(Two Phase Commitment Protocol)

当一个事务操作需要跨越多个分布式节点时,为了保障事务处理ACID的特性,就需要引入一个协调者TM(Transaction Manager)来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点统称为AP(Application)。TM负责调度AP的行为,并最终决定这些AP是否要把事务执行提交操作;因为事务是分两个阶段的所以叫2PC。

最终提交:   最终回滚:  

阶段一:执行事务操作阶段

1. 事务询问

协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各个参与者的响应。

2.执行事务

各个参与者节点接受到事务询问后开始执行事务操作,并将undo和redo操作信息记录到事务日志中,尽量把提交过程中的所有消耗时间的操作都在这个阶段完成,以确保后面的事务提交能够100%成功。

3.各个参与者向协调者反馈事务询问的响应

如果各个参与者成功执行了事务,则会反馈协调者yes的响应,表示事务可以提交;如果参与者没有成功执行事务,就反馈给协调者no的响应,表示事务不可以提交;这个过程有点类似协调者组织各个参与者对一次事务操作的投票表态过程,因此2pc协议的第一个阶段可以称之为“投票阶段”,即各个参与者投票表明是否需要执行接下去的事务提交操作。

阶段二:提交事务阶段

在这个阶段,协调者会根据各参与者的反馈情况来决定最终是否进行事务提交,正常情况下有两种情况:提交事务,中断事务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值