核心要点:3+2+1
3:三种服务器角色状态
- Leader: 处理客户端请求,向其他服务器追加同步日志信息,以及发送心跳保持自己的地位;
- Follower: 接收Leader发送的日志同步信息以持久化,接收来自Candiate的选举请求,返回支持选举信息;
- Candidate: Follower选择超时后(一定时间内没有收到来自Leader的心跳信息),变身为Candidate,并向其他服务器请求给自己投票以成为新Leader
2: 两种RPC接口
- RequestVote: Candidate向其他服务器请求选举投票,调用该RPC
- AppendEntries: Leader向其他服务器追加同步日志信息,同时也作为心跳信息,调用该RPC
1:term概念
通过使用term将时间进行切分,term是一个单调递增的整数,表示当前Leader治理之下的时代,新的Leader选举会导致term值的递增,从而形成一个逻辑时钟,Candidate选举发起选举时会递增该term值,选举成功则gaiterm值会用于指示消息的有效性,如Follower收到了一个带有老旧term值的消息,则会认为该消息无效(包括日志追加消息和选举投票请求消息)。如果旧的Leader或Candidate收到一个更高term的消息就会转为Follower状态。
选举过程(Leader Selection, Election)
选出一个新的Leader执行后续日志记录和同步操作。
触发选举:Follower的计时器经过一个超时(election timeout),期间没有收到Leader的信息(日志追加或心跳),并且也没有收到来自其他Candidate的选举投票信息,此时会触发新的选举,这个超时时间对于每一个服务器来说是随机设定的,以避免多个Candidate同时发起选举并导致都无法获得大多数选票。
Candidate的动作:触发选举后,Follower成为Candidate角色状态;然后增加自身的term值为currentTerm;并顺带投自己一票;重置选举计时器;向其他服务器发RequestVote选举投票请求RPC调用。
RequestVoteRPC内容:
- term: 当前Candidate的term值
- candidateID: 请求投票的candidate唯一标示
- lastLogIndex: candidate的最近一个日志index
- lastLogTerm: candidate的最后一个日志所属的term值
选举:一个服务器收到Candidate的选举RequestVote消息后,判断是否要给对方投票,标准是需要满足所有这些要求:
- RPC中的term值不能比自己的currentTerm小,如果term>currentTerm, 就更新自己的currentTerm
- 针对当前RPC中的term值,自己没有给其他Candidate投过票,一个term只能投一票,先来先得
- 日志有效性检查,RPC中candidate的最新日志必须至少和自己的日志一样新,如何判断一个日志较新:
- 日志项所属term值较大的
- term值一样,则日志index越大越新
选举结果:
- 自己获胜:如果Candidate收到大多数选票,则转移为Leader状态,马上对其他结点发送心跳信息(通过AppendEntries RPC),宣布获胜;
- 别人获胜:等选票的时候,如果candidate收到其他服务器的心跳信息,并且对方term值≥自己的currentTerm,就承认别人获胜,并更新自己的term值(如果需要),然后变成Follower状态。
- 没结果:如果一段选举超时时间经过了,自己没有赢,别人也没有赢,那就继续增加currentTerm值,发送RequestVote RPC开始新的选举,并且设置新的随机超时时间能够降低继续没有选举结果的概率。
日志复制
Leader正常工作时需要负责处理客户端请求,并保证集群中日志副本信息的一致性。