Etcd Raft 协议

Raft协议概览


etcd是raft协议的一个实现。

如果你使用过zookeeper这样的分布式键值对,你应该知道有个叫paxos的协议,paxos协议是挺复杂的,raft协议是paxos协议的简化版本。

大多数同意原则:一群人怎么达成原则,往往是通过投票,当多数人同意的时候就按照多数人投票的结果去执行。

在服务端一般是多个成员组成了一个集群,针对客户端任何的变更,这个变更首先会发到服务端,服务端在处理变更请求的时候,它会有一个一致性的模块,这个一致性模块会将这个变更请求发给其他所有的peer,请求经过一致性模块之后,请求就会转发到raft集群所有成员里面去,同时会将变更请求写入自己的日志模块当中,当其他成员处理了变更请求之后,也会将变更请求写入到自己的日志当中,并将结果返回给发起方,发起方会去做确认,只要有多数人确认了这个变更,那么这个变更就正真的生效了,最后会将状态写到状态机里面。

当我们去读取数据的时候,是从状态机里面去获取数据,状态机里面能够获取的数据都是经过多数人同意的。

所谓raft协议就是大多数人同意的原则来保证数据的一致性。

理解Raft协议


参考链接   Rafticon-default.png?t=M5H6http://thesecretlivesofdata.com/raft/

假设我们有个系统,这个系统是单节点系统,我们将这个节点当做数据库的server,我们要存一个值,这个一致性就非常简单了,客户端发什么过来就存什么,那么这样单节点的一致性就非常容易达成的,将发送过来的请求保存下来就一致了,因为没人人需要协商。

那么多节点的一致性如何去保证呢?这就是分布式一致性需要去解决的。

在raft协议里面,任何一个成员节点都可以是3个状态,第一个状态是follower,追随者,第二个是candidate候选人,现在身份还不确定,第三个是leader,它是发指令的领导者。

                       The Follower state,the Candidate state,or the Leader state.

当系统启动的时候,所有的节点都是follower身份启动的,也就是说这个时候是没有主的,大家都是想听别人的。

                                       All our nodes start in the follower state. 

每个follower要遵循一个原则,它要不停的接受leader发送过来的指令,也就是一个一个的心跳,如果一个follower在某个时间间隔内没有收到心跳,那么它会自动变为candidate。

             If followers don't hear from a leader then they can become a candidate. 

变为candidate之后,也就是需要投票了,现在没有leader,那么可以去试一试自己能不能当leader,所以follower没有leader来控制它,没有leader过来的心跳,那么它就变为candidate,变为candidate之后它就向其他节点发起投票请求说我想做leader,请给我投票。

其他的candidate有个原则,如果他们也没有leader,如果接收到正真投票请求,它就会自动投票。

                            The candidate then requests votes from other nodes.

candidate收到投票请求,既然你想做leader你就做吧,candidate就变为了leader。

                                             Nodes will reply with their vote.

上面整个过程叫做leader选举。

 The candidate becomes the leader if it gets votes from a majority of nodes.This process is called Leader Election.

 为什么要选leader呢?当往分布式系统里面写数据的时候,不能所有节点都去接受写的请求,如果都去写就会彼此冲突,这样就会导致整个系统的数据不一致,既然我能够从所有节点里面选出leader来,那么所有的写操作都应该从leader发起。

现在所有的写,比如一个客户端要去写5,这个时候请求会被发送到leader里面,这个时候写5是红色的,没有得到确认,只是在这里暂存了一下,然后在这里面写了一条log,写完了log并没有commit,所以这个写入不算成功,它只是临时记录了一下。

Each change is added as an entry in the node's log. This log entry is currently uncommitted so it won't update the node's value.

 因为leader要保持自己的地位,它就要不断的发送心跳,leader在发送心跳的时候,就会将set 5的值这个变更一起带过去。

To commit the entry the node first replicates it to the follower nodes...

所有的follower接收到这个请求之后,也会将这个信息存入到自己本地日志,并且回复消息给leader说,这条信息我确认了,我保存了。这个时候对leader来说收到多数人的投票,这条信息已经做了持久化的保存,那么对于leader来说这个日志其实是commit日志。

then the leader waits until a majority of nodes have written the entry. 

最后这个5就会作为已经确认的状态。那么下次心跳leader就会告诉follower,你们也去确认,最后这个写入就变成成功了的。

The leader then notifies the followers that the entry is committed.

The cluster has now come to consensus about the system state. 

当所有的信息确认完以后,这次写入才算成功,整个过程叫做日志复制

所以raft协议定义了两个行为,一个是选主,就是通过多数人投票来选择一个leader,第二个就是数据复制的过程,客户端发起一个写请求到leader这里,leader经过多次的心跳把这个信息带到follower里面,反复确认,最终达成数据一致,最后所有人的状态机里面这个5的值被写入了。

Raft如何选主


 下面是raft集群层面,每一个节点都有一个超时时间,这个时间是用来控制选举的一个超时时间,

In Raft there are two timeout settings which control elections.

选举超时时间是follower变为candidate的时间 

First is the election timeout. 

The election timeout is the amount of time a follower waits until becoming a candidate.

这个时间默认是150ms-300ms之间,也就是节点启动的时候,每一个人都会转一个随机的超时时间,系统启动的时候大家都是follower,这个时候没有leader。

The election timeout is randomized to be between 150ms and 300ms. 

每个人都随机计时,这个时候有人快有人慢,当这个超时时间过了之后就自动的变为candidate,然后就会开始新的投票。某个节点的timeout先转完,它就发起投票。

After the election timeout the follower becomes a candidate and starts a new election term... 

首先它会投票给自己,然后发送投票请求到其他人那里

...votes for itself... 

其他人那里收到了投票请求,它首先会同意,现在没有leader就投你,然后重置自己的timeout时间,然后重新去转了

...and sends out Request Vote messages to other nodes.

 

 If the receiving node hasn't voted yet in this term then it votes for the candidate...

...and the node resets its election timeout.

投票请求又会回个发起方,发起方这个时候就变为leader了,节点c就变为leader了,然后一直发心跳保持其地位,每次发心跳follower的timeout时间都会被重置,只要心跳一直发送的,他就不会变为candidate重新发起投票。它就会保持follower状态

The leader begins sending out Append Entries messages to its followers.

leader要一直保持心跳,无数据的写入的时候要保持地位,或者有数据写入的时候顺便把变更带下去,这个超时时间叫做heartbeat超时时间,它会一直保证心跳

These messages are sent in intervals specified by the heartbeat timeout.

如果是写入操作,每次写入都会跟着心跳往下走,follower确定这个心跳之后会告诉leader这次写入确认了,leader就会一直通过心跳来保证它的地位。

每次心跳发送过去,每个follower会去重置它的election timeout,这样整个集群就是稳定的,只要leader还活着,他就会保持leader的地位。

如果leader出现问题了怎么办?无论是宕机还是网络中断了,其他人没有的心跳,那么a节点又发起了投票,a发起投票马上被c确认了,因为集群是3个节点,b死掉了,a重新发起投票的时候,它得到了自己和nodec的认可,所以它就变成了新的leader。

作为一个新的leader,那么它的任期就是trem2,b死去的节点任期是1,a作为新任期的leader就会通过心跳来保证它的地位,只要半数以上可以确认这个投票,那么这个投票就是有效的,这个集群就能够正常工作。

节点为偶数个


有没有可能投票冲突的情况呢? 

b和a超时election timeout时间到,都同时发起了投票,

同时投票就可能它获取的票是等价的,因为当前集群是4个,a和b都先为自己投票1个,剩下的两个节点有个人投啊,有人投b,这样a和b都是2票

如果是这种情况,投票就是无效的,投票无效重新投票,这样就会产生无效的投票

 这就是当我们组建集群的时候都是1 3 5个member,没有说2 4个的。 

如果是奇数个,那么投票永远不会是均等的,永远是多余半数活或者少于半数,这样就有效的减少了无效的投票次数,在做选主的时候,数据库是不能写入数据的,因为没有leader,所以选主的时候,写数据都会阻塞的,如果有太多的选主那么会导致写入性能非常的慢,而且不稳定,所以规划集群的时候要是奇数个。

如果有数据的变更,它会发送给所有的follower,有个客户端发出了写入数据的要求,比如set 5发给了leader了,leader就会将变更通过下次心跳发出去,所有的follower写入自己的log,然后告知leader我已经写入log了,这个时候leader就可以发respones响应给客户端说我这次已经写入了。 

  

脑裂

没有leader的部分,上面三个节点,通过投票来选出了leader为e,下面leader为b,接受写的请求,那么b会发送给它所有的member,a可以确认,leader但是需要半数以上的确认它才能确认,因为不超过半数(只有它和a),那么这次写入是无法成功的,所以下面就变为了只读的部分,任何数的写入都是无法成功的。

但是上面部分的写入是没问题的,因为是多数可以确认的。

所以当一个集群被分裂之后,占多数的部分还是能够正常工作的,但是少数部分就停留在之前的状态了,任何数据的写入都不成功。

网络恢复了b和e都是leader,恢复之后他们都希望发送心跳来保持自己的地位,如果b发心跳去同步它的地位,之前它的term为1,那么term为2的candidate就不会认它,新任期的leader的数据会被同步到被分裂的少数派里面,而且所有的数据都会被同步掉。

当集群分裂的时候,多数派这边会代表集群的最新状态。

上面看到raft协议其实就两个目的,一个是选主,一个是复制。

 learner


为什么引入learner,在只有leader follower两种身份的时候,某些时候follower坏掉了,我要换一个新的上来,新的follower加入集群之后,它的数据差异会和leader差很远,因为它是空的(如果是它发起投票,因为它的commit比别人都要小很多,那么这次投票是无效的),这个时候数据相差太远,这样就会导致大量的数据复制,影响到leader心跳导致网络延时,这会导致leader心跳无法正常被follower都接收到。

你可以先以learner的身份加进来,先同步数据给你,你不要参与投票,直到和我数据一致了,才能变成follower,learner因为不参与投票,所以不影响原来的选举,当数据一致的时候,learner可以变为follower。

猜你喜欢

转载自blog.csdn.net/qq_34556414/article/details/125568739