我们之前定义了raft通信的基本数据格式以及rpc模式也生成了数据内容,这篇文章就要开始具体的操作了。

前言

由于本部分涉及到很多的判断过程,我们想要一蹴而就是不可能的,所以我们应该借鉴面向接口的思想,先定义好接口,外部操作直接调用,具体的操作我们以后再进行判断。

日志操作

在raft中在,最重要的判断就是日志的操作了吧,无论是增加日志,还是判断日志内容,抑或取出一个日志,我们都是对日志的操作,所以我们为了方便,先定义一个队日志的基本操作。
我们先自己考虑下有什么对日志的操作,目前至少这几个是要有的

  1. 日志内容对比:我们知道有prevLogTerm这些内容需要校验,这个是必须的
  2. 日志内容添加:Leader发来新的日志,Follower需要添加进来
  3. 取出日志最新任期,最新序号,以及已经执行的最新序号

大体就是以下几个内容

type Logger interface {
    Check(prevLogIndex int64, prevLogTerm int64, index int64, term int64) error
    Append(e *Entry) error
    FresherThan(index int64, term int64) bool
    Get(index int64) *Entry
    GetEntryForRequest(index int64) (*Entry, int64, int64)
    Index() int64
    LastIndex() int64
    Term() int64
}

其中entry就是日志的内容了,我们可以想下entry至少有以下结构

type Entry struct {
    CmdID int64
    Index int64
    Term  int64
    Data  []byte
}

好,关于日志的定义我们暂时先定义到这。

peer内容

我们从论文中可以看到,leader是需要维护一个数据结构的,这个数组里面有nextIndex和matchIndex,用来给不同的follower发送日志内容,因此我们定义这么一个结构

type Peer struct {
    ID         string
    NextIndex  int64
    MatchIndex int64
}

通信

我们应该还记得,上一篇文章我们定义了通信的基本要求,在这个地方我们需要二次封装下
首先是client端,我们知道,client端是可以生成client之后,直接调用函数进行操作的,但是client的生成需要地址,参数等,所以我们定义一个接口

type GRPCClient interface {
    RequestVote(address string, request *pb.VoteRequest) (*pb.VoteRespond, error)
    AppendEntries(address string, request *pb.EntryRequest) (*pb.EntryResponse, error)
}

这个就是我们client需要实现的内容了
server的话,不仅需要实现proto定义的service,还有定义这么几个接口,包括启动服务,停止服务,处理网络参数等

type GRPCServer interface {
    Address() string
    Start(n *Node) error
    Stop()
    Server() *grpc.Server
    Listener() net.Listener
}

状态机

我们还差最后一个部分,就是把日志内容执行的部分,我们收到了leader的指令,肯定是要存储的,就是有了一个接口

type Applyer interface {
    Apply(cmd *CommandRequest) error
}

这个CommandRequest是什么呢?就是具体的指令,有什么内容我们可以先不考虑,定义成一个空的结构体

type CommandRequest struct {
}

结束

我们目前定义了后续开发所需要的各种接口,工具与原料都准备好了,就准备开始造起来吧!

go raft 接口

发表新评论