一、客户端如何找到Leader

Raft算法规定客户端将所有申请发送给Leader。客户端启动的时候,如何晓得哪一个节点是Leader呢?具体办法是客户端随机筛选一个服务器进行通信,如果客户端选的服务器不是领导人,那么被筛选的服务器会回绝客户端的申请,并且提供它最近接管到的领导人的信息,即通过收到Leader发送的心跳的RPC失去Leader的网络地址。

还有一种状况,如果领导人曾经解体了,那么客户端的申请就会超时,客户端之后会再次重试随机筛选服务器的过程。

二、如何确保命令只执行1次

Raft算法是可能屡次执行同一条命令的,官网也举了一个例子:

如果领导人在提交了这条日志之后,然而在响应客户端之前解体了,那么客户端会和新的领导人重试这条指令,导致这条命令就被再次执行了,因为日志曾经提交,只是这个响应没有被发送到客户端。

解决方案

客户端对于每一条指令都赋予一个惟一的序列号,状态机跟踪每条指令最新的序列号和相应的响应。如果接管到一条指令,它的序列号曾经被执行了,那么就立刻返回后果,而不从新执行指令。

即由状态机来保障不反复执行,因为这不是Raft算法中的内容,而是如何保障幂等性了,行业内也有很多计划,大略原理都是为申请生成惟一ID,而后服务端判重,这里不详述了。

三、如何100%保障不脏读
后面说了所有读、写申请都发送Leader,那是不是意味着从Leader发动读申请的时候就不必做什么解决,能够间接返回了?这里可能有脏读的状况,咱们来举个例子:

以后集群有5个节点:A、B、C、D、E

假如以后Leader是A,当初因为网络起因导致网络分区,变成如下:

当初分成2个网络:A、B所在网络与C、D、E的网络不通,因为C、D、E与A所在网络不通,因而C、D、E会触发选举,并且节点数为3,满足集群节点少数的条件,因而能够选举出Leader;

如果此时客户端有新的申请发到左边的新集群,假如是一个批改key为test的操作并且提交了,而此时同样一个客户端申请发到A,读取key也为test,因为左边集群曾经有新操作了,间接从A的状态机里返回数据就是旧的了。

针对这个问题,官网给的计划是须要额定2个额定的措施来保障:

1、领导人必须有对于被提交日志的最新信息

即在它的任期里必须马上提交一条空白的日志条目,即心跳;

这段话的意思是在一个节点成为Leader之前,至多向少数节点发送一次心跳来进行确认日志状况,在没收到心跳响应之前是不能响应客户端的;

2、领导人在解决只读的申请之前必须查看本人是否曾经被破除了

具体实现是Leader在响应只读申请之前,先和集群中的大多数节点替换一次心跳信息来解决这个问题,即发送一次心跳的RPC,收到响应无误之后能力返回给客户端,即每次读申请要和少数成员做一次心跳以确认本人依然是 Leader。

回到下面的案例,因为网络分区了,客户端申请A的时候,A向成员发送心跳时,只有B能响应,没有达到少数成员的要求,因而不能响应给客户端。

下面这种状况也叫ReadIndex,有趣味的同学能够理解下相干实现。

四、一次申请残缺交互

最初以一次残缺的客户端申请来总结整个过程,包含客户端发送申请和Leader在什么时候响应;假如集群有3个节点:A、B、C,其中A以后为Leader,一次残缺的申请的过程如下:

1、客户端提交到申请至Leader;

2、Leader本人先保留日志,留神这里不提交;

3、Leader将日志同步给Follower,这里只画了1条线,即只同步给C,实际上是都会同步;

4、Follower收到日志后保留日志并响应给Leader;

5、Leader只有收到一个Follower的响应后马上将这条日志提交并利用到状态机中;

6、Leader利用到状态机实现后就能够返回给客户端了;

上图中后续的流程没有画,即Follower在什么时候提交日志,答案是Leader在下一次心跳的时候会将最新的commitIndex带上,Follower因而提交日志并利用到状态机中。