Leader选举
Leader选举会分两个过程
- 启动时候的Leader选举
- Leader崩溃的时候选举
服务器启动时的leader选举
每个节点启动的时候状态都是LOOKING,处于观望状态,接下来就开始进行选Leader流程
在集群初始化阶段,当有一台服务器Server1启动时,它本身是无法进行和完成Leader选举,当第二台服务器Server2启动时,这个时候两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程
- 每个Server发出一个投票,由于是初始化情况,Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票包含所推举的服务器的myid和ZXID,epoch,使用(myid, ZXID,epoch)来表示,此时Server1 的投票为(1, 0),Server2的投票为(2, 0),然后各自将 这个投票发给集群中其他机器
- 接受来自各个服务器的投票。集群的每个服务器收到 投票后,首先判断该投票的有效性,如检查是否是本 轮投票 (epoch)、是否来自LOOKING状态的服务器
- 处理投票。针对每一个投票,服务器都需要将别人的 投票和自己的投票进行PK,PK规则如下
- 优先检查ZXID,ZXID比较大的服务器优先作为Leader
- 如果ZXID相同,那么就比较myid,myid较大的服务器作为Leader服务器
对于 Server1 而言,它的投票是(1, 0),接收 Server2 的投票为(2, 0),首先会比较两者的ZXID,均为0,再 比较myid,此时Server2的myid最大,于是更新自 己的投票为(2, 0),然后重新投票,对于Server2而言, 它不需要更新自己的投票,只是再次向集群中所有机 器发出上一次投票信息即可。
- 统计投票。每次投票后,服务器都会统计投票信息, 判断是否已经有过半机器接受到相同的投票信息,对 于Server1、Server2而言,都统计出集群中已经有两 台机器接受了(2, 0)的投票信息,此时便认为已经选出 了Leader
- 改变服务器状态。一旦确定了Leader,每个服务器就 会更新自己的状态,如果是Follower,那么就变更为 FOLLOWING,如果是Leader,就变更为LEADING
Leader崩溃的时候选举
当集群中的 leader 服务器出现宕机或者不可用的情况时, 那么整个集群将无法对外提供服务,而是进入新一轮的 Leader 选举,服务器运行期间的 Leader 选举和启动时期 的Leader选举基本过程是一致的
- 变更状态。Leader挂后,余下的非Observer服务器 都会将自己的服务器状态变更为 LOOKING,然后开 始进入Leader选举过程。
- 每个Server会发出一个投票。在运行期间,每个服务 器上的ZXID可能不同,此时假定Server1的ZXID为 123,Server3的ZXID为122;在第一轮投票中,Server1 和Server3都会投自己,产生投票(1, 123),(3, 122), 然后各自将投票发送给集群中所有机器。接收来自各 个服务器的投票。与启动时过程相同
- 处理投票。与启动时过程相同,此时,Server1将会成 为Leader
- 统计投票。与启动时过程相同
- 改变服务器的状态。与启动时过程相同
补充epoch
epoch :可以理解为当前集群所处的年代或者周期,每个 leader 就像皇帝,都有自己的年号,所以每次改朝换代, leader 变更之后,都会在前一个年代的基础上加 1 。这样 就算旧的 leader 崩 溃 恢 复 之 后 ,也 没 有 人 听 他 的 了 ,因 为 follower 只听从当前年代的 leader 的命令
epoch的变化大家可以做一个简单的实验
- 启动一个zookeeper集群
- 在/tmp/zookeeper/VERSION-2 路径下会看到一个 currentEpoch文件。文件中显示的是当前的epoch
- 把 leader 节点停机,这个时候在看 currentEpoch 会有 变化。 随着每次选举新的leader,epoch都会发生变化
关于 ZXID
zxid,也就是事务id, 为了保证事务的顺序一致性,zookeeper 采用了递增的事 务 id 号(zxid)来标识事务。所有的提议(proposal)都 在被提出的时候加上了 zxid。实现中 zxid 是一个 64 位的 数字,它高32位是epoch(ZAB协议通过epoch编号来 区分 Leader 周期变化的策略)用来标识 leader 关系是否 改变,每次一个 leader 被选出来,它都会有一个新的 epoch=(原来的epoch+1),标识当前属于那个leader的 统治时期。低32位用于递增计数。