6.824系统 raft分布式选举


前言

这是6.824系统的raft算法的分布式选举部分,通过go语言进行实现。
来自MIT该系统的课程lab2

一、raft 算法分布式选举原理

Raft内部有一种心跳机制,如果存在leader,那么它就会周期性地向所有follower发送心跳,来维持自己的地位。如果follower-一段时间没有收到心跳,那么他就会认为系统中没有可用的leader了,然后开始进行选举。开始一个选举过程后,follower先增加自己的当前任期号,并转换到candidate.状态。然后投票给自己,并且并行地向集群中的其他服务器节点发送投票请求(RequestVote RPC)
在这里插入图片描述
投票的分裂处理
投票分裂 同时超时 并且得票一样 只要是candidate会不停地向没有回复的发起投票 一直投票 导致 在这个过程中 有其他已经投过票的 超时了
那么那个后面超时的 term++ term已经比前面两个 竞选者大了 小于新的竞选者 所以都会吧赞成票给新的竞选者 成为leader
另一个情况 都平票 那么就继续等待超时 然后继续选举

二、结构体说明与初始化

1、节点状态与投票状态

自身节点的状态只有三个 跟随者,领导者,竞选者,
在这里插入图片描述

type Status int
const (
	Follower Status =iota
	Candidate
	Leader
)

投票状态有正常投票,节点终止,投票国企,还有已经投过票了四种状态。
在这里插入图片描述

type VoteState int
const (
	Normal VoteState = iota //投票过程正常
	Killed                  //Raft节点已终止
	Expire                  //投票(消息\竞选者)过期
	Voted                   //本Term内已经投过票
)

2、日志结构体

日志结构体应该包含日志内容以及日志所在的任期两个陈媛在这里插入图片描述

type LogEntry struct {
   
	Term    int
	Command interface{
   }
}

3、raft节点

对于节点信息,应该包含所有的节点,以及自身节点的index,记录当前的任期,记录当前的任期把票投给了谁,日志条目数组,包含了状态机要执行的指令集,以及收到领导时的任期号。
在这里插入图片描述

还应该保存 节点是什么角色 以及超时时间以及计时器。在这里插入图片描述

type Raft struct {
   
	mu        sync.Mutex          // Lock to protect shared access to this peer's state
	peers     []*labrpc.ClientEnd // RPC end points of all peers
	persister *Persister          // Object to hold this peer's persisted state
	me        int                 // this peer's index into peers[]
	dead      int32               // set by Kill()
	// Your data here (2A, 2B, 2C).
	// Look at the paper's Figure 2 for a description of what
	// state a Raft server must maintain.
	currentTerm int  //记录单签的任期
	votedFor int   //记录当前的任期投票给谁了
	logs   []LogEntry //日志条目数组
	

	// 所有的servers经常修改的:
	// 正常情况下commitIndex与lastApplied应该是一样的,但是如果有一个新的提交,并且还未应用的话last应该要更小些
	commitIndex int // 状态机中已知的被提交的日志条目的索引值(初始化为0,持续递增)
	lastApplied int // 最后一个被追加到状态机日志的索引值

	// leader拥有的可见变量,用来管理他的follower(leader经常修改的)
	// nextIndex与matchIndex初始化长度应该为len(peers),Leader对于每个Follower都记录他的nextIndex和matchIndex
	// nextIndex指的是下一个的appendEntries要从哪里开始
	// matchIndex指的是已知的某follower的log与leader的log最大匹配到第几个Index,已经apply
	nextIndex  []int // 对于每一个server,需要发送给他下一个日志条目的索引值(初始化为leader日志index+1,那么范围就对标len)
	matchIndex []int // 对于每一个server,已经复制给该server的最后日志条目下标

	// 由自己追加的:
	status   Status        // 该节点是什么角色(状态)
	overtime time.Duration // 设置超时时间,200-400ms  固定的
	timer    *time.Ticker  // 每个节点中的计时器

	applyChan chan ApplyMsg // 日志都是存在这里client取(2B)
}

4、投票rpc

该rpc的请求结构体是竞选者发送的给其他节点的,应当包含以下内容,自身的任期,竞选者的index。竞选人日志条目最后索引,候选人最后日志条目的任期号。
而回复结构体 包含投票方的任期 以及 是否投票给你,还有就是投票的状态,是否投过票这些。
在这里插入图片描述

type RequestVoteArgs struct {
   
	// Your data here (2A, 2B).
	Term         int //	需要竞选的人的任期
	CandidateId  int // 需要竞选的人的Id
	LastLogIndex int // 竞选人日志条目最后索引
	LastLogTerm  int // 候选人最后日志条目的任期号
}


type RequestVoteReply struct {
   
	// Your data here (2A).
	Term        int       // 投票方的term,如果竞选者比自己还低就改为这个
	VoteGranted bool      // 是否投票给了该竞选人
	VoteState   VoteState // 投票状态
}

5、心跳rpc

首先设置一个全局的心跳超时时间

var HeartBeatTimeout = 120 * time.Millisecond

其发起的rpc

type AppendEntriesArgs struct {
   
	Term         int        // leader的任期
	LeaderId     int        // leader自身的ID
	PrevLogIndex int        // 预计要从哪里追加的index,因此每次要比当前的len(logs)多1 args初始化为:rf.nextIndex[i] - 1
	PrevLogTerm  int        // 追加新的日志的任期号(这边传的应该都是当前leader的任期号 args初始化为:rf.currentTerm
	Entries      []LogEntry // 预计存储的日志(为空时就是心跳连接)
	LeaderCommit int        // leader的commit index指的是最后一个被大多数机器都复制的日志Index
}

type AppendEntriesReply struct {
   
	Term     int                // leader的term可能是过时的,此时收到的Term用于更新他自己
	Success  bool               //	如果follower与Args中的PreLogIndex/PreLogTerm都匹配才会接过去新的日志(追加),不匹配直接返回false
	AppState AppendEntriesState // 追加状态
}

三、整体流程

1、整体

大体的流程如下,如果存在leader,那么它就会周期性地向所有follower发送心跳,来维持自己的地位。如果follower-一段时间没有收到心跳,那么他就会认为系统中没有可用的leader了,然后开始进行选举。
在这里插入图片描述

2、初始化

初始化每个raft包括写入所有的同行,自己的index,以及初始化自身的属性,比如说自己的状态一开始都是follower。设置随机产生的选举超时时间,一般设置在150-350ms。初始化完毕之后开一个协程进行控制这个raft

func Make(peers [
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值