前言
下面文章会以图形方式来解读raft协议,与其他作者文档相比,会更偏向工程的实现细节
适合读者:
- 想了解raft协议的懒人,不想读论文,不想看代码,不想动脑袋。。。
对一般人而言,了解raft协议,https://raft.github.io/这个网站都会接触到,它提供了:
- raft协议论文:https://raft.github.io/raft.pdf
- raft协议动画演示:http://thesecretlivesofdata.com/raft/
- raft协议代码实现:网站提供了git仓库地址,知名的如etcd/raft
动画部分建议先了解下,有助于下面的学习。
下面的raft流程交互和参数说明参考了etcd中的raft实现
1 初识
19世纪80年代,小红(id=1)、小黄(id=2)、小蓝(id=3)三位同学商量放假去哪儿玩,她们之间商议过程只能通过书信(会发生各种意外情况导致信件丢失或者回复缓慢)。我们一起看看三人之间是如何达成一致意见的。
先熟悉第一个概念,角色。每位同学都会分属下面三种角色之一:
角色 | 英文 |
---|---|
领导者 | leader |
跟随者 | follower |
候选者 | candidate |
故事开头,每个同学都是follower角色
2 领导者选举
2.1 选举超时时间 election timeout和任期term
既然是商量事情,总有一个要先提意见的,为了公平起见,给每位同学一个随机的超时时间(election timeout),谁先过了超时时间就准备开始发言
图中,小红150mm时间最短,小红最先超时
与超时时间类似,每位同学都有一个任期属性,正常情况下每发起一次选举,任期会自动加1,初始状态大家的任期都是0,图中的其他属性暂时忽略
2.2 小红的自我投票(MsgHup)和投票请求(MsgVote)
2.1节中小红最先到达超时时间,她的动作如下所示:
- 小红首先给自己发了一个Message1,type为MsgHup,告知自己要参与选举了
- 小红收到Message1后,将自己的term由0改为1,状态由StateFollower改为了StateCandidate,至此角色由follower转换为了candidate,顺便给自己投了一票(votes[1]=ture)
- 小红在term为1的任期内正式向小黄和小蓝发起投票请求(Message2.1和Message2.2) ,type为MsgVote
2.3 小黄和小蓝的回复(MsgVoteResp)
先看小黄的回复:
- 小黄接收到Message2.1后,将Message2.1的term(1)与自身的term(虚线框0)比较,发现自身的term小于Message2.1,继续保持自己的follower角色,同时将自身的term改为1
- 小黄向小红发送Message3.1进行回复,type为MsgVoteResp
小蓝的回复与小黄类似,回复Message3.2,见下图,不再赘述:
2.4 小红变为了leader
- 小红先收到了小黄的Message3.1,type为MsgVoteResp。这时候小红已经获得了两票,一票是自己投自己,一票是小黄投的(对应图中的votes[i]变量,votes[1]=ture,votes[2]=true),小红得票已经超过集群的半数,由此小红的状态从StateCadidate转变为了StateLeader
- 接着小红给自己创建了一个空日志条目,开始向小黄和小蓝发送Message4.1和Message4.2,type为MsgApp,即要求follower同步自己的数据
- 小红的prs[1].match_=1
小红收到了小黄的Message3.1后,由于满足半数投票,已经变为了leader角色,这时再收到小蓝的Message3.2,不再需要做任何操作
这时,小红在本地记录了一个空的Entry
- 此时的Entry只是临时记录,所以为虚线
3日志同步
3.1 小黄和小蓝的回应MsgAppResp
2.4节小红开始向小黄和小蓝发送Message4.1和Message4.2,下面是小黄和小蓝收到后的回复
- 小黄和小蓝收到信息后,各自也准备了一个空的Entry,并且向小红回复了type为MsgAppResp的Mesaage(Message5.1和Message5.2)
此时,三位同学的日志状态如下:
3.2 小红确认日志记录,继续发送MsgApp
- 小红收到小黄的Message5.1后,会设置prs[2].match_=1,至此已经有半数以上(2个人)同意进行日志提交,因此小红将自身的raftLog.committed_由0设置为1
- 小红处理完Message5.1后,会继续向小黄发送Message6.1,type为MsgApp,继续同步日志记录。与Message4.1不同的是,此时index、logterm、commit由0变为了1
- 小红继续受到小蓝的Message5.2,会设置prs[3].match_=1,处理完后同样会发送Message6.2给小蓝,index、logterm、commit同样为1
此时日志状态,小红的日志由虚线变为实线
3.3 小黄和小蓝确认日志记录
- 小黄收到小红Message6.1后,根据msg中的commit为1,修改自己的commit为1,然后发送type为MsgAppResp的Message7.1
- 同理,小蓝也会修改自己的commit为1,然后返回type为MsgAppResp的Message7.2
此时日志状态,小蓝和小黄由虚线变为实线
3.4 小红的收尾
- 当小红收到Message7.1后,检查msg中的index和本地的prs[2].match_,两者相等,不做任何操作
- 同理,小红收到Message7.2后,检查msg中的index和本地的prs[3].match_,两者相等,不做任何操作