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