6.4.1 领导者选举

Paxos 算法中“节点众生平等”,每个节点都可以发起提案。多个提议者并行发起提案,是活锁、以及其他异常问题的源头。那如何不破坏 Paxos 的“节点众生平等”基本原则,又能在提案节点中实现主次之分,限制节点不受控的提案权利?Raft 对此的设计是明确领导者、通过选举机制“分享”提案权利。

先来看 Raft 算法中节点的分类:

  • 领导者(Leader):负责处理所有客户端请求,将请求转换为“日志”复制到其他节点,不断地向所有节点广播心跳消息:“你们的领导还在,不要发起新的选举”。
  • 跟随者(Follower):接收、处理领导者的消息,并向领导者反馈日志的写入情况。当领导者心跳超时时,他会主动站起来,推荐自己成为候选人。
  • 候选人(Candidate):候选人属于过渡角色,他向所有的节点广播投票消息,如果他赢得多数选票,那么他将晋升为领导者;

联想到现实世界中的领导人都有一段不等的任期。自然,Raft 算法中也对应的概念 —— “任期”(term)。Raft 中的任期是一个递增的数字,贯穿于 Raft 的选举、日志复制和一致性维护过程中。

  • 选举过程:任期确保了领导者的唯一性。在一次任期内,只有获得多数选票的节点才能成为领导者。
  • 日志一致性:任期号会附加到每条日志条目中,帮助集群判断日志的最新程度。
  • 冲突检测:通过比较任期号,节点可以快速判断自己是否落后,并切换到跟随者状态。

图 6-11 Raft 通过任期区分和管理集群中领导者的生命周期

图 6-12 概述了 Raft 集群 Leader 选举过程。

图 6-12 Raft 选举过程

初始状态下,所有的节点处于跟随者状态。如果跟随者在某个时限(通常是 150-300 毫秒的随机超时时间)未收到领导者心跳,则触发触发选举。节点的角色转为 候选者,任期号递增,然后向其他节点广播“投票给我”的消息(RequestVote RPC)。

RequestVote RPC 消息示例如下:

{
  "term": 5, // 候选者的当前任期号,用于通知接收方当前选举属于哪个任期。
  "candidateId": 3, // 候选者的节点 ID,标识请求投票的节点。
  "lastLogIndex": 12, // 候选者日志的最后一条日志的索引,用于比较日志的完整性。
  "lastLogTerm": 4//候选者日志的最后一条日志的任期号,用于进一步比较日志的新旧程度。
}

其他节点收到投票消息后,根据下面的条件判断是否投票:

  • 候选者的日志至少与投票者的日志一样新(根据最后一条日志的任期号和索引号判断)。
  • 当前节点尚未在本任期投票。

RequestVote 响应的示例如下:

{
  "term": 5, //接收方的当前任期号,用于告知候选者最新的任期号。如果候选者发现该值比自己大,会转为跟随者。
  "voteGranted": true//是否投票给候选者,true 表示同意,false 表示拒绝。
}

如果候选者获得多数(超过半数)投票,即成为领导者。之后,领导者向其他节点广播心跳消息,维持领导者地位。如果没有获得多数票,进入下一轮选举,任期号递增,重新发起投票。如果选举过程中收到任期号更高的心跳或投票请求,则转为跟随者。

基于“少数服从多数”的原则,获得多数选票的领导者代表了整个集群的意志。现在,你思考:“代表集群意志的领导者发起提案时,是否还需要 Paxos 第一轮中 “准备阶段” ?”。

总字数:1031
Last Updated:
Contributors: isno