raft算法练习-模拟三节点的分布式选举

本文深入探讨了Raft一致性算法的实现细节,包括Leader选举、日志复制等关键流程,并通过Go语言示例代码展示了如何在分布式系统中实现这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

raft理论知识(网络上有很多,所以理论不是这篇文章的核心)

Raft是一个用于管理日志一致性的协议。它将分布式一致性分解为多个子问题:Leader选举(Leader election)、日志复制(Log replication)、安全性(Safety)、日志压缩(Log compaction)等。同时,Raft算法使用了更强的假设来减少了需要考虑的状态,使之变的易于理解和实现。Raft将系统中的角色分为领导者(Leader)、跟从者(Follower)和候选者(Candidate):

Leader:接受客户端请求,并向Follower同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志。

Follower:接受并持久化Leader同步的日志,在Leader告之日志可以提交之后,提交日志。

Candidate:Leader选举过程中的临时角色。

Raft要求系统在任意时刻最多只有一个Leader,正常工作期间只有Leader和Followers。Raft算法将时间分为一个个的任期(term),每一个term的开始都是Leader选举。在成功选举Leader之后,Leader会在整个term内管理整个集群。如果Leader选举失败,该term就会因为没有Leader而结束。

Leader选举的过程
  Raft 使用心跳(heartbeat)触发Leader选举。当服务器启动时,初始化为Follower。Leader向所有Followers周期性发送heartbeat。如果Follower在选举超时时间内没有收到Leader的heartbeat,就会等待一段随机的时间后发起一次Leader选举。

每一个follower都有一个时钟,是一个随机的值,表示的是follower等待成为leader的时间,谁的时钟先跑完,则发起leader选举。

Follower将其当前term加一然后转换为Candidate。它首先给自己投票并且给集群中的其他服务器发送 RequestVote RPC。结果有以下三种情况:

赢得了多数的选票,成功选举为Leader;

收到了Leader的消息,表示有其它服务器已经抢先当选了Leader;

没有服务器赢得多数的选票,Leader选举失败,等待选举时间超时后发起下一次选举。

实现(本文核心)

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/rpc"
	"sync"
	"time"
)

//模拟三节点的分布式选举


//定义常量3
const raftCount = 3



//声明leader对象
type Leader struct {
	//任期
	Term int
	//领导编号
	LeaderId int
}


//创建存储leader的对象
//最初任期为0,-1代表没编号
var leader = Leader{0, -1}


//声明raft节点类型
type Raft struct {
	//锁
	mu sync.Mutex
	//节点编号
	me int
	//当前任期
	currentTerm int
	//为哪个节点投票
	votedFor int
	//当前节点状态
	//0 follower  1 candidate  2 leader
	state int
	//发送最后一条消息的时间
	lastMessageTime int64
	//当前节点的领导
	currentLeader int

	//消息通道
	message chan bool
	//选举通道
	electCh chan bool
	//心跳信号
	heartBeat chan bool
	//返回心跳信号
	hearbeatRe chan bool
	//超时时间
	timeout int
}


func main() {
	//过程:创建三个节点,最初是follower状态
	//如果出现candidate状态的节点,则开始投票
	//产生leader

	//创建三个节点
	for i := 0; i < raftCount; i++ {
		//定义Make() 创建节点
		Make(i)
	}

	//对raft结构体实现rpc注册
	rpc.Register(new(Raft))
	rpc.HandleHTTP()
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal(err)
	}

	//防止选举没完成,main结束了
	for {;
	}
}



//创建节点
func Make(me int) *Raft {
	rf := &Raft{}
	//编号
	rf.me = me
	//给0  1  2三个节点投票,给谁都不投
	rf.votedFor = -1
	//0 follower
	rf.state = 0
	rf.timeout = 0
	//最初没有领导
	rf.currentLeader = -1
	//设置任期
	rf.setTerm(0)
	//通道
	rf.electCh = make(chan bool)
	rf.message = make(chan bool)
	rf.heartBeat = make(chan bool)
	rf.hearbeatRe = make(chan bool)
	//随机种子
	rand.Seed(time.Now().UnixNano())

	//选举的逻辑实现
	go rf.election()
	//心跳检查
	go rf.sendLeaderHeartBeat()

	return rf
}


func (rf *Raft) setTerm(term int) {
	rf.currentTerm = term
}



//设置节点选举
func (rf *Raft) election() {
	//设置标签
	var result bool
	//循环投票
	for {
		timeout := randRange(150, 300)
		//设置每个节点最后一条消息的时间
		rf.lastMessageTime = millisecond()
		select {
		case <-time.After(time.Duration(timeout) * time.Millisecond):
			fmt.Println("当前节点状态为:", rf.state)
		}
		result = false
		//选leader,如果选出leader,停止循环,result设置为true
		for !result {
			//选择谁为leader
			result = rf.election_one_rand(&leader)
		}
	}
}



//产生随机值
func randRange(min, max int64) int64 {
	//用于心跳信号的时间等
	return rand.Int63n(max-min) + min
}


//获取当前时间的毫秒数
func millisecond() int64 {
	return time.Now().UnixNano() / int64(time.Millisecond)
}



//选leader
func (rf *Raft) election_one_rand(leader *Leader) bool {
	//超时时间
	var timeout int64
	timeout = 100
	//投票数量
	var vote int
	//用于是否开始心跳信号的方法
	var triggerHeartbeat bool
	//当前时间戳对应的毫秒
	last := millisecond()
	//定义返回值
	success := false

	//首先,要成为candidate状态
	rf.mu.Lock()
	rf.becomeCandidate()
	rf.mu.Unlock()

	//开始选
	fmt.Println("start electing leader")
	for {
		//遍历所有节点进行投票
		for i := 0; i < raftCount; i++ {
			//遍历到不是自己,则进行拉票
			if i != rf.me {
				//其他节点,拉票
				go func() {
					//其他节点没有领导
					if leader.LeaderId < 0 {
						//操作选举通道
						rf.electCh <- true
					}
				}()
			}
		}
		//设置投票数量
		vote = 0
		triggerHeartbeat = false
		//遍历所有节点进行选举
		for i := 0; i < raftCount; i++ {
			//计算投票数量
			select {
			case ok := <-rf.electCh:
				if ok {
					//投票数量+1
					vote ++
					//返回值success
					//大于总票数的一半
					success = vote > raftCount/2
					//成领导的状态
					//如果票数大于一半,且未出发心跳信号
					if success && !triggerHeartbeat {
						//选举成功
						//发心跳信号
						triggerHeartbeat = true

						rf.mu.Lock()
						//真正的成为leader
						rf.becomeLeader()
						rf.mu.Unlock()

						//由leader向其他节点发送心跳信号
						//心跳信号的通道
						rf.heartBeat <- true
						fmt.Println(rf.me, "号节点成为了leader")
						fmt.Println("leader发送心跳信号")
					}
				}
			}
		}
		//间隔时间小于100毫秒左右
		//若不超时,且票数大于一半,且当前有领导
		if (timeout+last < millisecond() || (vote >= raftCount/2 || rf.currentLeader > -1)) {
			//结束循环
			break
		} else {
			//没有选出leader
			select {
			case <-time.After(time.Duration(10) * time.Millisecond):
			}
		}
	}
	return success
}


//修改节点为candidate状态
func (rf *Raft) becomeCandidate() {
	//将节点状态变为1
	rf.state = 1
	//节点任期加1
	rf.setTerm(rf.currentTerm + 1)
	//设置为哪个节点投票
	rf.votedFor = rf.me
	//当前没有领导
	rf.currentLeader = -1
}



func (rf *Raft) becomeLeader() {
	//节点状态变为2,代表leader
	rf.state = 2
	rf.currentLeader = rf.me
}





//设置发送心跳信号的方法
//只考虑leader没有挂的情况
func (rf *Raft) sendLeaderHeartBeat() {
	for {
		select {
		case <-rf.heartBeat:
			//给leader返回确认信号
			rf.sendAppendEntriesImpl()
		}
	}
}


//返回给leader的确认信号
func (rf *Raft) sendAppendEntriesImpl() {
	//判断当前是否是leader节点
	if rf.currentLeader == rf.me {
		//声明返回确认信号的节点个数
		var success_count = 0

		//设置返回确认信号的子节点
		for i := 0; i < raftCount; i++ {
			//若当前不是本节点
			if i != rf.me {
				go func() {
					//子节点有返回
					//rf.hearbeatRe <- true

					//rpc
					rp, err := rpc.DialHTTP("tcp", "127.0.0.1:8080")
					if err != nil {
						log.Fatal(err)
					}
					//接收服务端发来的消息
					var ok = false
					er := rp.Call("Raft.Communication", Param{"hello"}, &ok)
					if er != nil {
						log.Fatal(err)
					}
					if ok {
						//rpc通信的情况下,子节点有返回
						rf.hearbeatRe <- true
					}
				}()
			}
		}
		//计算返回确认信号的子节点,若子节点个数>raftCount/2,则校验成功
		for i := 0; i < raftCount; i++ {
			select {
			case ok := <-rf.hearbeatRe:
				if ok {
					//记录返回确认信号的子节点的个数
					success_count++
					if success_count > raftCount/2 {
						fmt.Println("投票选举成功,校验心跳信号成功")
						log.Fatal("程序结束")
					}
				}
			}
		}
	}
}


//分布式通信
type Param struct {
	Msg string
}

//等待客户端消息
func (r *Raft) Communication(p Param, a *bool) error {
	fmt.Println(p.Msg)
	*a = true
	return nil
}


运行结果:

在这里插入图片描述

本文由ManGe原创,转载请附上原创地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值