异度部落格

学习是一种生活态度。

0%

Zookeeper源码分析-Zookeeper Leader选举算法

当 Leader 崩溃或者 Leader 失去大多数的 Follower,这时候 zk 进入恢复模式,恢复模式需要重新选举出一个新的 Leader,让所有的 Server 都恢复到一个正确的状态。Zookeeper 中 Leader 的选举采用了三种算法:

  • LeaderElection
  • FastLeaderElection
  • AuthFastLeaderElection

并且在配置文件中是可配置的,对应的配置项为 electionAlg。

背景知识

Zookeeper Server 的状态可分为四种:

  • LOOKING:寻找 Leader
  • LEADING:Leader 状态,对应的节点为 Leader。
  • FOLLOWING:Follower 状态,对应的节点为 Follower。
  • OBSERVING:Observer 状态,对应节点为 Observer,该节点不参与 Leader 选举。

成为 Leader 的必要条件: Leader 要具有最高的 zxid;当集群的规模是 n 时,集群中大多数的机器(至少 n/2+1)得到响应并 follow 选出的 Leader。

心跳机制:Leader 与 Follower 利用 PING 来感知对方的是否存活,当 Leader 无法相应 PING 时,将重新发起 Leader 选举。

术语

zxid:zookeeper transaction id, 每个改变 Zookeeper 状态的操作都会形成一个对应的 zxid,并记录到 transaction log 中。 这个值越大,表示更新越新。

electionEpoch/logicalclock:逻辑时钟,用来判断是否为同一次选举。每调用一次选举函数,logicalclock 自增 1,并且在选举过程中如果遇到 election 比当前 logicalclock 大的值,就更新本地 logicalclock 的值。

peerEpoch: 表示节点的 Epoch。

LeaderElection 选举算法

LeaderElection 是 Fast Paxos 最简单的一种实现,每个 Server 启动以后都询问其它的 Server 它要投票给谁,收到所有 Server 回复以后,就计算出 zxid 最大的哪个 Server,并将这个 Server 相关信息设置成下一次要投票的 Server。该算法于 Zookeeper 3.4 以后的版本废弃。

选举算法流程如下:

  1. 选举线程首先向所有 Server 发起一次询问(包括自己);
  2. 选举线程收到回复后,验证是否是自己发起的询问(验证 xid 是否一致),然后获取对方的 id(myid),并存储到当前询问对象列表中,最后获取对方提议的 leader 相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;
  3. 收到所有 Server 回复以后,就计算出 zxid 最大的那个 Server,并将这个 Server 相关信息设置成下一次要投票的 Server;
  4. 线程将当前 zxid 最大的 Server 设置为当前 Server 要推荐的 Leader,如果此时获胜的 Server 获得多数 Server 票数, 设置当前推荐的 leader 为获胜的 Server,将根据获胜的 Server 相关信息设置自己的状态,否则,继续这个过程,直到 leader 被选举出来。

leader-election

通过流程分析我们可以得出:要使 Leader 获得多数 Server 的支持,则 Server 总数必须是奇数 2n+1,且存活的 Server 的数目不得少于 n+1.

异常问题的处理:

  1. 选举过程中,Server 的加入
    当一个 Server 启动时它都会发起一次选举,此时由选举线程发起相关流程,那么每个 Serve r 都会获得当前 zxi d 最大的哪个 Serve r 是谁,如果当次最大的 Serve r 没有获得 n/2+1 个票数,那么下一次投票时,他将向 zxid 最大的 Server 投票,重复以上流程,最后一定能选举出一个 Leader。
  2. 选举过程中,Server 的退出
    只要保证 n/2+1 个 Server 存活就没有任何问题,如果少于 n/2+1 个 Server 存活就没办法选出 Leader。
  3. 选举过程中,Leader 死亡
    当选举出 Leader 以后,此时每个 Server 应该是什么状态(FLLOWING)都已经确定,此时由于 Leader 已经死亡我们就不管它,其它的 Fllower 按正常的流程继续下去,当完成这个流程以后,所有的 Fllower 都会向 Leader 发送 Ping 消息,如果无法 ping 通,就改变自己的状为(FLLOWING ==> LOOKING),发起新的一轮选举。
  4. 选举完成以后,Leader 死亡
    处理过程同上。
  5. 双主问题
    Leader 的选举是保证只产生一个公认的 Leader 的,而且 Follower 重新选举与旧 Leader 恢复并退出基本上是同时发生的,当 Follower 无法 ping 同 Leader 是就认为 Leader 已经出问题开始重新选举,Leader 收到 Follower 的 ping 没有达到半数以上则要退出 Leader 重新选举。

FastLeaderElection 选举算法

由于 LeaderElection 收敛速度较慢,所以 Zookeeper 引入了 FastLeaderElection 选举算法,FastLeaderElection 也成了 Zookeeper 默认的 Leader 选举算法。

FastLeaderElection 是标准的 Fast Paxos 的实现,它首先向所有 Server 提议自己要成为 leader,当其它 Server 收到提议以后,解决 epoch 和 zxid 的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息。FastLeaderElection 算法通过异步的通信方式来收集其它节点的选票,同时在分析选票时又根据投票者的当前状态来作不同的处理,以加快 Leader 的选举进程。

算法流程

数据恢复阶段

每个 ZooKeeper Server 读取当前磁盘的数据(transaction log),获取最大的 zxid。

发送选票

每个参与投票的 ZooKeeper Server 向其他 Server 发送自己所推荐的 Leader,这个协议中包括几部分数据:

  • 所推举的 Leader id。在初始阶段,第一次投票所有 Server 都推举自己为 Leader。
  • 本机的最大 zxid 值。这个值越大,说明该 Server 的数据越新。
  • logicalclock。这个值从 0 开始递增,每次选举对应一个值,即在同一次选举中,这个值是一致的。这个值越大说明选举进程越新。
  • 本机的所处状态。包括 LOOKING,FOLLOWING,OBSERVING,LEADING。

处理选票

每台 Server 将自己的数据发送给其他 Server 之后,同样也要接受其他 Server 的选票,并做一下处理。

如果 Sender 的状态是 LOOKING

  • 如果发送过来的 logicalclock 大于目前的 logicalclock。说明这是更新的一次选举,需要更新本机的 logicalclock,同事清空已经收集到的选票,因为这些数据已经不再有效。然后判断是否需要更新自己的选举情况。首先判断 zxid,zxid 大者胜出;如果相同比较 leader id,大者胜出。
  • 如果发送过来的 logicalclock 小于于目前的 logicalclock。说明对方处于一个比较早的选举进程,只需要将本机的数据发送过去即可。
  • 如果发送过来的 logicalclock 等于目前的 logicalclock。根据收到的 zxid 和 leader id 更新选票,然后广播出去。

当 Server 处理完选票后,可能需要对 Server 的状态进行更新:

  • 判断服务器是否已经收集到所有的服务器的选举状态。如果是根据选举结果设置自己的角色(FOLLOWING or LEADER),然后退出选举。
  • 如果没有收到没有所有服务器的选举状态,也可以判断一下根据以上过程之后更新的选举 Leader 是不是得到了超过半数以上服务器的支持。如果是,那么尝试在 200ms 内接收下数据,如果没有心数据到来说明大家已经认同这个结果。这时,设置角色然后退出选举。

如果 Sender 的状态是 FOLLOWING 或者 LEADER

  • 如果 LogicalClock 相同,将数据保存早 recvset,如果 Sender 宣称自己是 Leader,那么判断是不是半数以上的服务器都选举它,如果是设置角色并退出选举。
  • 否则,这是一条与当前 LogicalClock 不符合的消息,说明在另一个选举过程中已经有了选举结果,于是将该选举结果加入到 OutOfElection 集合中,根据 OutOfElection 来判断是否可以结束选举,如果可以也是保存 LogicalClock,更新角色,退出选举。

fast-leader-election

具体实现

数据结构

本地消息结构:

1
2
3
4
5
6
7
8
9
10
static public class Notification {
long leader; //所推荐的Server id

long zxid; //所推荐的Server的zxid(zookeeper transtion id)

long epoch; //描述leader是否变化(每一个Server启动时都有一个logicalclock,初始值为0)

QuorumPeer.ServerState state; //发送者当前的状态
InetSocketAddress addr; //发送者的ip地址
}

网络消息结构:

1
2
3
4
5
6
7
8
9
10
11
12
static public class ToSend {

int type; //消息类型
long leader; //Server id
long zxid; //Server的zxid
long epoch; //Server的epoch
QuorumPeer.ServerState state; //Server的state
long tag; //消息编号

InetSocketAddress addr;

}

线程处理

每个 Server 都一个接收线程池和一个发送线程池, 在没有发起选举时,这两个线程池处于阻塞状态,直到有消息到来时才解除阻塞并处理消息,同时每个 Server 都有一个选举线程(可以发起选举的线程担任)。

  • 接收线程的处理
    notification: 首先检测当前 Server 上所被推荐的 zxid,epoch 是否合法(currentServer.epoch <= currentMsg.epoch && (currentMsg.zxid > currentServer.zxid || (currentMsg.zxid == currentServer.zxid && currentMsg.id > currentServer.id))) 如果不合法就用消息中的 zxid,epoch,id 更新当前 Server 所被推荐的值,此时将收到的消息转换成 Notification 消息放入接收队列中,将向对方发送 ack 消息。
    ack: 将消息编号放入 ack 队列中,检测对方的状态是否是 LOOKING 状态,如果不是说明此时已经有 Leader 已经被选出来,将接收到的消息转发成 Notification 消息放入接收对队列

  • 发送线程池的处理
    notification: 将要发送的消息由 Notification 消息转换成 ToSend 消息,然后发送对方,并等待对方的回复,如果在等待结束没有收到对方法回复,重做三次,如果重做次还是没有收到对方的回复时检测当前的选举(epoch)是否已经改变,如果没有改变,将消息再次放入发送队列中,一直重复直到有 Leader 选出或者收到对方回复为止。
    ack: 主要将自己相关信息发送给对方

  • 选举线程的处理
    首先自己的 epoch 加 1,然后生成 notification 消息,并将消息放入发送队列中,系统中配置有几个 Server 就生成几条消息,保证每个 Server 都能收到此消息,如果当前 Server 的状态是 LOOKING 就一直循环检查接收队列是否有消息,如果有消息,根据消息中对方的状态进行相应的处理。

AuthFastLeaderElection 选举算法

AuthFastLeaderElection 算法同 FastLeaderElection 算法基本一致,只是在消息中加入了认证信息,该算法在最新的 Zookeeper 中也建议弃用。

Example

下面看一个 Leader 选举的例子以加深对 Leader 选举算法的理解。

  1. 服务器 1 启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是 LOOKING 状态.
  2. 服务器 2 启动,它与最开始启动的服务器 1 进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以 id 值较大的服务器 2 胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是 3),所以服务器 1,2 还是继续保持 LOOKING 状态.
  3. 服务器 3 启动,根据前面的理论分析,服务器 3 成为服务器 1,2,3 中的 Leader,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的 Leader.
  4. 服务器 4 启动,根据前面的分析,理论上服务器 4 应该是服务器 1,2,3,4 中最大的,但是由于前面已经有半数以上的服务器选举了服务器 3,所以它只能是 Follower.
  5. 服务器 5 启动,同 4 一样,Follower.

参考资料

http://blog.csdn.net/xhh198781/article/details/10949697

http://blog.cnsolomo.com/ld/liunx/nginx/264.html

http://blog.sina.com.cn/s/blog_3fe961ae01012jod.html

http://blog.csdn.net/xhh198781/article/details/6619203

http://csrd.aliapp.com/?p=162

http://codemacro.com/2014/10/19/zk-fastleaderelection/