一、日志存储

每条日志存储内容如下:

term:领导人所在任期

利用操作内容:由客户端发送的申请,须要被复制状态机(replicated state machine)执行的命令,如上是一个KV零碎,每一次的操作是对某个key的内容。

二、日志状态

日志大略分以下几个状态:

1、初始化

即刚被退出到零碎中

2、被提交

如果一条日志被少数节点收到,则该日志会被转为被提交,即能够被利用到状态机。

3、已利用

即曾经利用到状态机了,能够返回给客户端了。

三、与日志相干的音讯

1、AppendEntries RPC

这个音讯由Leader收回,有2个作用:

A、将客户端发送的命令而产生的日志发送给Follower,从而推动日志达到统一的状态;

B、心跳,表明集群中存在Leader,不必发动选举;

第一个场景是承受客户端命令后收回,并且等大部分Follower接管到才返回给客户端;

第二个场景由Leader定时收回;

相干规定如下:

1、对于Leader

  • 于一个追随者来说,如果上一次收到的日志索引大于将要收到的日志索引(nextIndex):通过AppendEntries RPC将 nextIndex 之后的所有日志条目发送进来
  • 如果发送胜利:将该追随者的 nextIndex和matchIndex更新
  • 如果因为日志不统一导致AppendEntries RPC失败:nextIndex递加并且从新发送(5.3 节)
  • 如果存在一个满足N > commitIndex和matchIndex[i] >= N并且log[N].term == currentTerm的 N,则将commitIndex赋值为 N

下面一条规定好了解,以下面的举例来说:

假如节点从上到下别离是A、B、C、D、E,以后Leader是节点A,后面说过Leader针对每一个Follower会记录nextIndex和matchIndex,咱们假如最现实的状况,即A针对B记录的nextIndex是9,matchIndex是8,所以心跳音讯中的nextIndex是9,B收到音讯会查看nextIndex之前的日志是否在本地存在,因6-8的日志不存在,因而返回false,A因而将nextIndex回退至6才匹配上,而后将6-8的日志发送给B,并且将matchIndex和nextIndex也一并更新。

规定2比拟难了解:

如果存在一个满足N > commitIndex和matchIndex[i] >= N并且log[N].term == currentTerm的 N,则将commitIndex赋值为 N

重点是加粗的内容,说简略点以后Leader不能间接提交日志的term不为本人的,即不能间接提交后任的日志,举个例子:

这是5个节点的集群,服务器编号别离是 S1-S5,最下面是日志索引,每个框内的数字示意日志的term,最底下的字母示意场景,别离为a-e。

场景a:S1是Leader,term为2,并且将index为2的日志复制到S2上;

场景b:S1挂了,S5入选为Leader,term增长为3,S5在index为2的地位上接管到了新的日志;

场景c:S5挂了,S1入选为Leader,term增长为4,S1将index为2、term为2的日志复制到了S3上,此时曾经满足过半数了。

问题就在场景c:此时term为4,之前term为2的日志达到过半数了,S1是提交该日志呢还是不提交

如果S1提交的话,则index为2、term为2的日志就被利用到状态机中了,就不可吊销了;

此时S1如果挂了,来到场景d,S5是能够被选为leader的,因为依照之前的log比对策略来说,S5的最初一个log的term是3,比S2、S3、S4的最初一个log的term都大。

一旦S5被选举为leader,即场景d,S5会复制index为2、term为3的日志到上述机器上,这时候就会造成之前S1曾经提交的index为2的地位被从新笼罩,因而违反了一致性。

如果S1不提交,而是等到term4中有过半的日志了,而后再将之前的term的日志一起提交,即处于e场景,S1此时挂的话,S5就不能被选为leader了,因为S2、S3的最初一个log的term为4,比S5的3大,所以S5获取不到投票,进而S5就不可能去笼罩上述的提交。

总结下:Leader不能间接提交后任的日志,哪怕后任日志曾经被少数节点收到了,而是等等以后任期的日志被大多数节点接管后做提交后,间接的提交了后任的日志。

四、其它技术细节

1、AppendEntries RPC音讯参数

term:领导人的任期号

leaderId:领导人标识

prevLogIndex:前一条日志索引号

prevLogTerm:前一 条日志任期号

具体要复制的音讯

laderCommitIndex:leader的commitIndex

2、Follower收到响应处理过程

首先是term的查看,这块是公共逻辑:

查看对方的term是否比本人小,如果是则返回本人的term,并返回失败 ;

如果本人的term比对方大,则不论以后什么角色变为Follower,并更新term为对方的term,并增加日志;

如果term和本人相等,则只有Follower、Candidate才变为Follower,更新Term,并增加日志,对于Leader应该返回失败。

3、对于日志太大的问题

对于1个7*24小时的服务,如果日志始终追加,最终磁盘空间必定不够的,有问题复原也太慢,这就生产了日照的需要,Raft算法大设计的时候就思考到了这个问题,这个留到前面章节剖析。