5.11 Raft的GO实现

package main

import (
“sync”
“math/rand”
“fmt”
“time”
“log”
)

//3节点的分布式一致性演示

//设置节点的个数为3
//main中创建节点可声明
const raftCount = 3

//声明leader的存储对象
type AppendEntiresArgs struct {
//声明任期
Term int
//设置领导编号
LeaderId int
}

//创建一个存储领导的对象
//0代表还没上任,-1是没编号
var args = AppendEntiresArgs{0, -1}

//声明节点类型
type Raft struct {
//命名规范是Raft源码中的
//线程锁
mu sync.Mutex
//节点编号
me int
//当前任期
currentTerm int
//为哪个节点投票
votedFor int
//当前节点状态
// 0 follower ,1 candidate ,2 leader
state int
//发送最后一条消息的时间
//这里保存int64类型的时间戳
lastMessageTime int64
//设置当前节点的领导
currentLeader int

//通道的功能:线程间的传参
//节点间发送消息的通道
message chan bool
//选举通道
eclectCh chan bool
//心跳信号通道
heartBeat chan bool
//返回心跳信号通道
heartbeatRe chan bool
//超时时间
timeout int

}

func main() {

//过程:创建3节点,开始是follower状态
//若有candidate状态节点,则投票
//产生leader

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

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

//main结束了,子线程没法顺利选举
for {;
}

}

//通过Make函数创建Raft节点对象
//me为节点编号
//返回一个Raft对象的指针
func Make(me int) *Raft {
//取地址,用new也可以
rf := &Raft{}
//编号为0,1,2
rf.me = me
//给谁投票?这里设置为不投
//因为编号是0,1,2,所以-1是谁都不投
rf.votedFor = -1
//当前节点状态,0
rf.state = 0
rf.timeout = 0
//当前节点领导,没领导
rf.currentLeader = -1
//创建一个设置任期的方法
//第0任期
rf.setTerm(0)
//创建通道对象
rf.eclectCh = make(chan bool)
rf.message = make(chan bool)
rf.heartBeat = make(chan bool)
rf.heartbeatRe = make(chan bool)
//每创建一个节点,设置随机数种子
rand.Seed(time.Now().UnixNano())

//以上写完,运行可以创建三个节点
//但现在还没有投票和选举的功能

//下面这两套代码,同时跑在三个节点
//设置节点投票的方法
go rf.election()
//设置leader节点发送心跳信号的方法
go rf.sendLeaderHeartBeat()

//返回节点
return rf

}

//创建Raft对象的方法
func (rf *Raft) setTerm(term int) {
rf.currentTerm = term
}

//设置节点投票的方法
func (rf *Raft) election() {
for {
//设置超时时间,产生一个随机值
timeout := randRange(150, 300)
//设置每个节点的时间
//创建一个millisecond方法
rf.lastMessageTime = millisecond()
//select理解为用于通信的switch语句
select {
//延时等待
case <-time.After(time.Duration(timeout) * time.Millisecond):
//打印当前节点的状态
fmt.Println(“当前节点状态为:”,rf.state)
}
//设置标签
var result bool
result = false
//当选出leader时,result置为true,则停止循环
for !result {
//选择谁为leader
//args为领导的存储对象,上面定义的
//创建election_one_round方法
result = rf.election_one_round(&args)
}
}
}

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

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

//选择谁为leader
//一直往下走流程就行,这段代码是3个节点在跑
func (rf *Raft) election_one_round(args *AppendEntiresArgs) bool {
var timeout int64
var done int
//定义是否开始心跳信号的产生
var triggerHeartbeat bool
last := millisecond()
timeout = 100
//定义标签
//用于返回值
success := false

//修改当前Raft的状态为candidate状态
rf.mu.Lock()
rf.becomeCandidate()
rf.mu.Unlock()
//开始选取leader
fmt.Println("start electing leader")
for {
	for i := 0; i < raftCount; i++ {
		//me:节点编号
		//如果遍历到的不是自己,则拉选票
		if i != rf.me {
			//拉选票
			go func() {
				// args:存储leader的对象
				// LeaderId:领导者的编号
				//因为上面创建时LeaderId默认为-1
				if args.LeaderId < 0 {
					//设置此节点开始选举
					//eclectCh:选举通道
					rf.eclectCh <- true
				}
			}()
		}
	}
	//设置投票数量
	//目前没人投票,为0
	done = 0
	//triggerHeartbeat:是否开始心跳信号的产生
	triggerHeartbeat = false
	// 3-1
	for i := 0; i < raftCount-1; i++ {
		//计算投票数量
		select {
		case ok := <-rf.eclectCh:
			if ok {
				//投票数量加1
				done++
				//前面定义的,用于方法返回值
				//得到投票数是否大于一半节点
				success = done > raftCount/2
				//得到大于一半的投票,且未触发心跳信号
				if success && !triggerHeartbeat {
					//选举成功了
					//开始触发心跳信号
					triggerHeartbeat = true
					rf.mu.Lock()
					////修改节点为leader状态
					rf.becomeLeader()
					rf.mu.Unlock()
					//由leader向其他节点发送心跳信号
					//由心跳信号通道发送心跳信号
					rf.heartBeat <- true
					fmt.Println("leader发送心跳信号")
				}
			}
		}
	}
	//若间隔时间小于100毫秒,则进行延时操作
	if ((done >= raftCount/2 || rf.currentLeader > -1) || timeout+last < millisecond()) {
		//选出节点后,结束循环,返回success为true
		break
	} else {
		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)
//votedFor:为哪个节点投票
//me:节点编号
rf.votedFor = rf.me
//当前没有领导
rf.currentLeader = -1
}

//修改节点为leader状态
func (rf *Raft) becomeLeader() {
//节点状态为2
rf.state = 2
//当前leader为节点自己
rf.currentLeader = rf.me
}

//设置leader节点发送心跳信号的方法
//发送数据,达到数据一致性的目标
//这里只考虑leader没有挂的情况
func (rf *Raft) sendLeaderHeartBeat() {
for {
select {
//其他节点接收到leader发送的心跳信号
//当有leader时才进行心跳检查
case <-rf.heartBeat:
rf.sendAppendEntriesImpl()
}
}
}

//返回给leader确认信号
func (rf *Raft) sendAppendEntriesImpl() {
//判断是否是leader节点
//上面有for循环遍历,可以遍历到leader节点
if rf.currentLeader == rf.me {
//记录返回确认信号的节点个数
var success_cout int = 0

	//设置返回确认信号的子节点
	for i := 0; i < raftCount; i++ {
		//若当前不是本节点
		if i != rf.me {
			go func() {
				//heartbeatRe返回心跳信号的通道设为true
				rf.heartbeatRe<-true
				//将返回信号发送给leader
				//rp, err := rpc.DialHTTP("tcp", "127.0.0.1:8080")
				//if err != nil {
				//	log.Fatal(err)
				//}
				//接收服务器返回的信息
				//var ok bool = false
				//最后面定义的方法,传方法的参数Param
				//er := rp.Call("Raft.Communication", Param{"hello"}, &ok)
				//if er != nil {
				//	log.Fatal(er)
				//}
				//if ok {
				//	rf.heartbeatRe <- true
				//}
			}()
		}
	}

	//计算返回确认信号的子节点,若子节点的个数>raftCount/2,则校验成功
	for i := 0; i < raftCount-1; i++ {
		select {
		case ok := <-rf.heartbeatRe:
			if ok {
				//记录返回确认信号的节点个数
				success_cout++
				if success_cout > raftCount/2 {
					fmt.Println("投票选取成功,校验心跳信号成功")
					//结束程序进程
					log.Fatal("程序结束")
				}
			}
		}
	}
}

}

//提问:现在是一台机器,如果真是的分布式多个节点怎么调用呢?
//添加RPC,代码可以分布式运行

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

//等待客户端消息
//Communication:通信
//注意Param大写,第二个参数必须加*号,规定的
//func(r *Raft)Communication(p Param, a *bool)error {
// fmt.Println(p.Msg)
// *a = true
// return nil
//}

学院Go语言视频主页
https://edu.csdn.net/lecturer/1928

[清华团队带你实战区块链开发]

(https://ke.qq.com/course/344443?tuin=3d17195d)
扫码获取海量视频及源码 QQ群:721929980
在这里插入图片描述

你可能感兴趣的:(区块链,共识算法)