聊聊分布式一致性协议---Raft协议

Posted 咸X蛋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊分布式一致性协议---Raft协议相关的知识,希望对你有一定的参考价值。

Raft是分布式一致性协议,与Paxos相比,它更简单,更易理解。本文将对Raft协议进行简单介绍。


基本原理


Raft中每个节点存在三个状态:leader、candidate和follower。每个节点在不同状态下,需要保存如下一些参数:


每个节点需要持久存在的参数:

currentTerm:节点最后任期ID,其从0递增;

votedFor:当前任期内的候选人ID,若没有则为null;

log[]:存储日志内容;每条日志包括任期号和命令内容;


每个节点不稳定存在的参数:

commitIndex:已经提交的最大日志索引;

lastApplied:被状态机执行的最大日志索引;

日志从被提交到执行,通常有一定的时间间隔。这两个参数都是从0开始递增。


leader节点不稳定存在的参数:

nextIndex[]:保存各follower节点中,待发送的日志索引,初始化为leader最后一条日志索引+1;

matchIndex[]:保存各follower节点中,已经成功复制的日志索引,初始化为0;

这两个参数在完成选举后,进行初始化。


约束条件:

每个节点需要遵守:

1、若commitIndex>lastApplied,则,自增lastApplied,同时,将log[lastApplied]应用到状态机;

2、若请求或响应的term>currentTerm,则,设置currentTerm=term,同时将自己切换为follower状态;


follower节点需要遵守:

1、响应来自leader或candidate节点的请求;

2、若election time时间内,未收到leader的同步(心跳)信息,或者candidate的投票信息,则,将自己切换为candidate状态;


candidate节点需要遵守:

1、一旦成为candidate,则必须开始选举:

        a:将currentTerm自增1;

        b:选举自己为leader;

        c:重置election time;

        d:向其他所有节点发送选举请求;

2、若收到大多数节点的投票,则,自己变为leader;

3、若收到一个新leader发来的同步日志请求,则,自己变为follower;

4、若超过election time未决,则,发起新一轮选举;


leader节点需要遵守:

1、一旦成为leader,需要向其他节点发送心跳包,同时,在空间时间重复发送,防止发起选举;

2、若收到来自客户端的请求,需要先将日志追加到本地,待应用到状态机后,响应客户端;

3、若某follower节点的最新日志索引>=nextIndex,则,需要往该follower节点,发送nextIndex以后所有的日志:

            a:若成功,则,更新nextIndex和matchIndex;

            b:若因为日志不一致失败,则,将nextIndex递减,重试;

4、若有N>commintIndex,且大多数matchIndex[i]>=N,同时log[N].term==currentTerm,则,将commitIndex设定为N。


基本特性:

1、一个任期内只存在一个leader;

2、leader只会追加日志,不会覆盖和删除日志;

3、如果两条日志的索引和任期号相同,则认为这两条日志是一致的,同时,该索引位置之前所有日志都是一致的;

4、如果一条日志在当前任期被提交,那么,它将出现在,比当前任期号更大的所有任期内;

5、如果一个节点已经将日志应用到状态机;则,其他节点不会在该索引位置应用其他日志;

Raft保证这些特性在任何情况下都成立。


Leader选举


当follower节点在election time内,没有收到leader的任何信息(日志同步信息或者心跳),则发起选举。选举时,follower节点将自身当前任期号(currentTerm)自增1,并向其他节点发起选举自己为leader的请求,同时也将自己的状态转为candidate。直到满足下列三个条件:1)自己成为leader;2)其他节点成为leader;3)本轮中没有任何节点成为leader。


选举接口(RequestVote RPC)定义:

参数:

term:候选者任期号;

candidateId:请求投票的候选人id;

lastLogIndex:候选人最新日志索引;

lastLogTerm:候选人最新日志对应的任期号;


返回值:

term:当前任期号,用于候选人更新自己;

voteGranted:返回true表示同意选择该候选人为leader;


响应逻辑:

1、若term值小于currentTerm,则返回follower;

2、若votedFor为null,或者votedFor为candiatedId,并且候选者日志是最新日志(根据lastLogIndex和lastLogTerm判断),则同意选举candidateId为leader;


关键点:

1、follower节点频繁发起选举,导致不可用的问题。需要设置合理的election time,一般设置为比节点平均响应时间大一个数量级;

2、产生多个leader,且投票数均未超过半数,即选举分裂。每个节点使用一个随机的election time,一般取150-300ms之间的随机值;

3、已提交日志丢失问题。选举时,只能选举拥有全部提交日志者为leader;


日志同步


选举完成之后,leader节点需要向follower节点同步日志。同时,所有的客户端请求,都需要通过leader节点完成。


日志同步接口(AppendEntries RPC)定义(该接口也用来发送心跳包):

参数:

term:leader的任期号;

leaderId:leader节点ID,以便在客户端连接follower节点时,能将请求转到leader节点;

prevLogIndex:最新日志之前的日志索引;

prevLogTerm:最新日志之前的日志所属任期号;

entires[]:日志信息,当为心跳包时,entires为null;可以存储多条日志;

leaderCommit:leader节点的commitIndex;


返回值:

term:当前任期号;用于leader更新自己的任期号;

success:follower节点上存在,能满足prevLogIndex和prevLogTerm的日志时,返回true;


响应逻辑:

1、若term<currentTerm,返回false;

2、若节点日志中prevLogIndex与prevLogTerm不匹配,返回false;

3、若存在日志与待更新日志相冲突(索引号相同,term不相同),则删除该日志及其后续所有日志;

4、追加在节点日志中不存在的所有日志;

5、若leaderCommit>commitIndex,则,commitIndex=min(leaderCommit, 最新的日志索引);


关键点:

1、Raft算法中日志的同步是单方向的,即,日志只会从leader节点流往follower节点;

2、leader与follower的日志不一致时,日志同步需要进行一致性检查。具体操作方法是,递减follower节点的nextIndex,直到找到一个索引点,使得这个索引点上,leader和follower日志一致,然后将follower中,该索引点以后的日志,全部删除。leader向follower同步该索引点开始的所有日志。


集群变更


当集群成员发生变更时,raft采用两阶段法。集群首先将配置信息切换为共同一致(joint consensus)状态,一旦共同一致被提交后,系统再切到新配置。其中,配置信息同样是通过日志复制完成。在处于共同一致状态时,客户端的请求,需要同时得到新旧配置两个集群大多数达成一致才能进行提交。但是仍然有三个问题需要解决:


1、新加入节点,需要同步大量日志。因此,在同步日志阶段,该节点处于无投票权状态;


2、集群leader可能不是新配置成员。因此,在些期间,leader只负责同步日志,但是不会被认为自己是大多数之一。


3、被移除的节点,可能因为收不到新配置下leader发送的心跳,而频繁发起选举,并且一起递增自己的currentTerm。避免这个问题的办法是,在发起选举前,每个节点都需要确认在最小的election time内是否收到leader的心跳包,如果有,则不会响应选举。


日志压缩


随着客户端操作的增加,日志信息会越来越大,Raft采用的的方式是通过状态快照+增量日志的方式进行解决。


总结


Raft协议强化了leader的特性,使得该协议,更容易理解。同时,日志连续性,也确保了同步逻辑的简单。


参考资料

https://ramcloud.atlassian.net/wiki/download/attachments/6586375/raft.pdf

以上是关于聊聊分布式一致性协议---Raft协议的主要内容,如果未能解决你的问题,请参考以下文章

大佬,我懵了!PaxosRaft不是一致性算法/协议?

白话分布式一致性协议 | raft协议

Raft一致性协议

raft--分布式一致性协议

分布式一致性算法--Raft

分布式一致性协议 Raft