一. 服务器启动时期的Leader选举
1:处理投票规则
投票信息:
1.logicalclock(electionEpoch):本地选举周期,每次投票都会自增
2.epoch(peerEpoch):选举周期,每次选举最终确定完leader结束选举流程时会自增(真正zxid的前32位)
3.zxid:数据ID,每次数据变动都会自增(真正zxid的后32位,zxid一共64位)
4.sid:该投票信息所属的serverId
5.leader:提议的leader(被提议的server的serverId,即sid)
投票比较规则:
1.epoch大的胜出,否则进行步骤2
2.zxid大的胜出,否则进行步骤3
3.sid大的胜出
针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
· 优先检查ZXID。ZXID比较大的服务器优先作为Leader。
· 如果ZXID相同,那么就比较myid(服务器的唯一标识)。myid较大的服务器作为Leader服务器。
2:选举流程简述
每个Server发出一个投票。初始情况Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,
- 此时Server1的投票为(1, 0)
- Server2的投票为(2, 0)
- 然后各自将这个投票发给集群中其他机器(即:当前只有Server1,Server2,即Server1发送给Server2自己的投票结果,Server2发送给Server1自己的投票结果)。
- 对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0)
- 但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
- Server3启动 ,给自己投票,投票结果 :(3,0),同时与之前启动的服务器Server1,Server2交换信息
- 对于Server1而言,它的投票是上面更新 pk过的结果:(2, 0),对于Server2而言,它的投票是上面更新 pk过的结果:(2, 0),Server1、Server2此时接收到Server3的结果为:(3,0),Server1、Server2没pk过Server3的结果,所以Server1、Server2分别更新自己的投票为(3,0),重新发起投票,Server1(3,0)、Server2(3,0),对于Server3而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
- 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(3, 0)的投票信息,此时便认为server3是Leader。
- 服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
-
服务器Server4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3;服务器4并更改状态为FOLLOWING;
- 服务器5启动,后面的逻辑同服务器4成为小弟。
- 可以这么理解:
- 目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
- 在集群初始化阶段,当有一台服务器Server1启动时,该Server1处于Looking状态,其单独无法进行和完成Leader选举;当第二台服务器Server2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程如下:
- 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
- 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
- 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,Server1、Server2更新自己的投票为:(3, 0),此时投票数正好大于半数,然后重新投票,即:服务器3成为领导者,服务器1,2成为小弟。
- 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
- 服务器5启动,后面的逻辑同服务器4成为小弟。
二、服务器运行时期的Leader选举
在Zookeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入,此时也不会影响Leader,但是一旦Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮Leader选举,其过程和启动时期的Leader选举过程基本一致。假设正在运行的有Server1、Server2、Server3三台服务器,当前Leader是Server2,若某一时刻Leader挂了,此时便开始Leader选举。选举过程如下:
- 变更状态。Leader挂后,余下的非Observer服务器都会讲自己的服务器状态变更为LOOKING,然后开始进入Leader选举过程。
- 每个Server会发出一个投票。在运行期间,每个服务器上的ZXID可能不同,此时假定Server1的ZXID为123,Server3的ZXID为122;在第一轮投票中,Server1和Server3都会投自己,产生投票(1, 123),(3, 122),然后各自将投票发送给集群中所有机器。
- 接收来自各个服务器的投票。与启动时过程相同。
- 处理投票。与启动时过程相同,此时,Server1将会成为Leader(Server1的zxid>Server1的zxid)
- 统计投票。与启动时过程相同。
- 改变服务器的状态。与启动时过程相同。
三、Leader选举实现细节
1. 服务器状态
服务器具有四种状态,分别是LOOKING、FOLLOWING、LEADING、OBSERVING。
- LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入Leader选举状态。
- FOLLOWING:跟随者状态。表明当前服务器角色是Follower。
- LEADING:领导者状态。表明当前服务器角色是Leader。
- OBSERVING:观察者状态。表明当前服务器角色是Observer。
2. 投票数据结构
每个投票中包含了两个最基本的信息,所推举服务器的SID和ZXID,投票(Vote)在Zookeeper中包含字段如下
- id:被推举的Leader的SID。
- zxid:被推举的Leader事务ID。
- logicalclock(electionEpoch):逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序列,每次进入新一轮的投票后,都会对该值进行加1操作。(本地选举周期,每次投票都会自增)
- state:当前服务器的状态。