P7 Raft(2)
Leader发送消息时日志插槽的维护过程
服务器 | 10(日志插槽) | 11(日志插槽) | 12(日志插槽) | 13(日志插槽) |
---|---|---|---|---|
S1 | 3 | |||
S2 | 3 | 3 | 4 | |
S3 | 3 | 3 | 5 |
假如在任期6的时候,S3是leader,这时来到一条消息,将填入13这个日志插槽中,并将这一消息发送给S2以及S1,在发送这个消息的过程中,会有prevLogIndex以及prevLogTerm这两个字段,根据当前情况,这两个字段的值分别为12以及5,然后消息到达S2以及S1时将进行对比,由于S2在12日志插槽中的prevLogTerm为5与消息发送来的字段不相等,所以S2将拒绝这个AppendEntries,S1这个日志插槽内没有条目,因此两个都将发送拒绝请求给S3.
leader会维护一个nextIndex,即【S2】 = 12,【S1】 = 12
服务器 | 10(日志插槽) | 11(日志插槽) | 12(日志插槽) | 13(日志插槽) |
---|---|---|---|---|
S1 | 3 | |||
S2 | 3 | 3 | 4 | |
S3 | 3 | 3 | 5 | 6 |
S3收到拒绝请求之后,将会根据nextIndex中的信息,把12插槽以及13两个插槽的信息一起发送出去进行复制,同时prevLogIndex以及prevLogTerm这两个字段为11和3,S2进行比对后字段相同,则会将12插槽中原来的数据给删除,复制为从S3接收到的数据。S1仍然会返回错误。
服务器 | 10(日志插槽) | 11(日志插槽) | 12(日志插槽) | 13(日志插槽) |
---|---|---|---|---|
S1 | 3 | |||
S2 | 3 | 3 | 5 | 6 |
S3 | 3 | 3 | 5 | 6 |
收到S1的错误请求后,将会对nextIndex中的信息进行更新,即【S1】 = 11,然后会继续发送3个日志插槽中的信息,这时S1字段相等,进行复制并给S3返回成功的信息。
S3更新nextIndex为 【S2】= 14,【S1】= 14
服务器 | 10(日志插槽) | 11(日志插槽) | 12(日志插槽) | 13(日志插槽) |
---|---|---|---|---|
S1 | 3 | 3 | 5 | 6 |
S2 | 3 | 3 | 5 | 6 |
S3 | 3 | 3 | 5 | 6 |
那么,问题来了,为什么S2删除12日志插槽中的信息是被允许的呢?
A:因为这条日志没有被大多数serves所拥有,因此即使日志插槽中有记录,这条数据也不会被执行更新,也没有给客户端肯定的回答。
节点成为领导者的条件
拥有最长日志的服务器不一定就能作为leader
eg:
服务器 | |||
---|---|---|---|
S1 | 5 | 6 | 7 |
S2 | 5 | 8 | |
S3 | 5 | 8 |
候选者被投同意的条件:
- 在last entry中拥有更高的term
- 在last entry中拥有相同的term,拥有日志条目的长度更长。
持久化
在单点电源故障或者整个集群电源故障发生后,之前保存的state能够使serve再次运行。
Log:通过log来重新执行
CurrentTerm:如上表中的例子,S1宕机后,需要知道term号为8而不是6
votedFor:只给一个server投票
为什么服务器重启后,commitIndex,lastApplied,nextIndex,matchIndex可以丢弃
- commitIndex
- lastApplied:目前执行的进度
- nextIndex
- matchIndex
日志压缩、快照
当日志量太大时,raft会使应用程序通过日志中的某一个点所对应的application state来制作一个快照(KV),然后就可以将日志中该点之前的数据丢弃。
快照程序需要由应用程序提供,因为每个应用程序的状态都不相同。