我们来说下Hashicorp Raft的实际使用的API,这是一个共性的问题,就是对于一个程序,如何在实际场景中将其组合起来

为了更好的理解这个API接口,我们将其连接起来,我们通过创建 增加 移除 集群节点,查看集群节点状态 4个场景,来循序渐进的了解Hashicorp Raft的API接口

1,创建Raft节点

在Hashicorp Raft之中,可以通过NewRaft()函数,来创建Raft节点,NewRaft()是非常核心的函数,是Raft节点的抽象实现,其原型如下

func NewRaft(

conf *Config,

fsm FSM,

logs LogStore,

stable StableStore,

snaps SnapshotStore,

trans Transport) (*Raft, error)

NewRaft()函数有几个参数

分别是Config 配置信息

FSM 有限状态机

LogStore 存储Raft的日志

StableStore 稳定存储,存储Raft集群的节点信息

SnapshotStore 快照存储,存储节点的快照信息

Transpot Raft节点之间的通信通道

这些参数决定来了Raft节点的配置 存储 通信 状态机操作等核心信息,那么我们需要注意如何创建这些参数信息

Config是节点的配置信息,可以通过函数DefaultConfig()来创建默认配置信息,也可以按需进行创建.比如

生产环境部署时候,将LogLevel从DEBUG调整为WARM或者ERROR

如果是部署环境中网络延迟高,可以调大HeartbeatTimeout的值,比如,从1s到2s,避免频繁的领导者选举

那么FSM这个自动机呢?其实内部包含了Apply(*Log),Snapshot() Restore(io.ReadCloser)三个函数,分别是日志应用,生成快照,根据快照恢复数据

FSM是日志处理的核心实现

LogStore存储的是Raft日志,可以用raft-boltdb来实现存储,持久化存储数据,Raft-boltdb是一个专门的持久化设计

logStore, err := raftboltdb.NewBoltStore(filepath.Join(raftDir, “raft-log.db”))

传入一个文件路径

StableStore存储的是节点的关键信息,比如,任期编号,最新投票时候的任期编号

stableStore, err := raftboltdb.NewBoltStore(filepath.Join(raftDir, “raft-stable.db”))

SnapshotStore是快照信息,压缩后的日志数据,在HashicorpRaft中提供了三种快照存储,分别是DiscardSnapshotStore,不存储,进行快照的忽略,相当于dev/null,用于测试

FileSnapshotStore(文件持久化存储)

InmemSnapshotStore(内存存储,不持久化,重启后,数据会丢失)

建议使用FileSnapshotStore实现快照,使用文件持久化存储,避免快照数据的丢失

snapshots, err := raft.NewFileSnapshotStore(raftDir, retainSnapshotCount, os.Stderr)

NewFileSnapshotStore()函数支持3个参数,除了指定日志的存储路径,还可以指定保留的快照数量,以及输出方式,可以指定为标准错误IO输出

最后一个Transport是指的是Raft集群内部之间的通信机制,节点之间通过这个通道进行日志同步,领导者选举的问题,Hashicorp Raft支持两种方式,

基于TCP协议的TCPTransport,可以跨机器网络通信

或者基于内存的InmemTransport,可以不走网络,在内存内部通过Channel来通信(主要用于测试)

如何使用TCP来进行网络通讯呢?

具体的代码如下

addr, err := net.ResolveTCPAddr(“tcp”, raftBind)

transport, err := raft.NewTCPTransport(raftBind, addr, maxPool, timeout, os.Stderr)

NewTransport支持5个参数,指定创建连接的必需信息,

要绑定的地址信息,raftBind addr 连接池的大小 maxPool,超时时间 timeout 日志输出的方式

上面就是TCP的发送方式了,如何使用NewRaft函数,可以直接调用,并获取到对应的Raft对象,像是如下的样子

raft,err := raft.NewRaft(config,(*storeFSM)(s),logStore,stableStore,snapshots,transporit)

这样,我们就已经创建好了Raft节点,打好了基础,但是我们需要实现的是一个多节点的集群,还需要让节点启动,节点启动,还需要创建新的节点,并将其加入到集群中,如何操作呢?

2.增加集群节点,集群的启动分为两个状态,一种是初次组建集群,此时还没有领导者,

或者是一种集群正常启动,此时领导者已经正式保存了

应对第一种的启动方式,我们应该使用bootstrap的方式将其进行引导的启动,然后启动后成为领导者

raftNode.BootstrapCluster(configuation)

这个函数只需要传入一个参数,就是Raft集群的配置信息,这个时候只有一个节点,所以配置信息为这个节点的地址信息

后续的节点在启动的时候,直接向着第一个节点发送加入集群的请求,然后进行加入到集群中,其他人在收到这个地址信息后,调用AddVoter()把新节点加入到集群中,代码如下

raftNode.AddVoter(id,

addr, prevIndex, timeout)

在AddVoter之中,需要设置服务器的ID信息和地址信息,其他的参数使用默认值0

id 服务器ID信息

addr 地址信息

prevIndex 前一个集群配置的索引值,默认为0

timeout 等待日志响应时间,默认为0

这样,我们就尝试加入了集群节点,但是如果节点出现了异常,需要进行移除节点,替换节点,如何使用呢

3.移除集群节点

可以通过RemoveServer()函数来移除节点,具体代码如下

raftNode.RemoveServer(id, prevIndex, timeout)

RemoveSever的参数,支持id信息,服务器的ID信息

prevIndex 前一个集群配置的索引值,一般设置为0,也是用默认值

timeout 在完成日志项添加前,需要最长等待多久,一般设置为0,使用默认值

RemoveServer()函数必须在领导者上运行,不然会报错

4查看节点状态

分布式系统中,日常调试的时候,节点的状态信息很重要,比如在Raft系统中,如果想要抓包分析写请求,必须要知道哪个节点是领导者节点,地址信息是什么,在Raft中,只有领导者可以处理写请求

可以再任意的节点上,调用Raft.Leader()函数,查看当前领导者的地址信息,也可以在Raft.State()函数中,查看节点的状态,是跟随者,候选人还是领导者,不过,Raft.State()函数返回的是32位无符号整数,还是建议在代码中使用,如果想在日志或者命令行接口中查看节点状态信息,使用RaftState.String()函数来进行查看节点状态吧

这样,本章之中,在集群节点中,我们了解了Hashicorp Raft的常用API接口

除了提到的Raft-boltdb作为LogStore和StableSotre,可以调用NewInmemStore()创建内存型存储,测试比较方便,重新执行程序进行测试的时候,不需要手动请求理数据

可以通过NewInmemTransport()函数,实现内存类型通讯的接口,测试比较方便,集群通过内存进行通讯,运行在一台机器上

Raft,Stats()函数,查看集群的内部统计信息,比如节点状态,任期编号,节点数,这在时候调试或者确认节点运行状态很有用

以集群节点为核心,讲解了Hashicorp Raft常用的API接口,对于开发一个分布式西永已经有了一定的感觉了,然后我们说下如何自己去实现一个KV存储的系统

非Leader节点如何启动的呢?

当节点加入到集群的时候,需要先调用raft.NewRaft()启动后,将自己的节点信息同步给领导者,有领导者调用AddVoter()函数,将这个节点加入到集群中,之后调用raft.NewRaft()启动节点就行,节点先作为跟随者进行启动,如果没有领导者,发生了心跳超时,选举出新的领导者

对于内存内的节点如何配置

1.在启动节点的函数中,使用pid作为唯一区分,在内存中,进行内存地址的交流

2.利用端口号加localhost作为本地的不同标识,进行相关的启动

发表评论

邮箱地址不会被公开。 必填项已用*标注