etcd-raft 源码分析
source link: https://zhuanlan.zhihu.com/p/49792009
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
etcd-raft 源码分析
本篇文章主要分析etcd-raft的代码实现,关于raft协议的细节,参考:
Etcd-raft的实现包含以下功能:
- Leader election
- Log replication/Log compaction
- Membership changes/Leader transfer
- Linearizable / Lease read
值得注意的是,etcd-raft并没有实现消息的网络发送和接收,而是把消息保存在内存中,需要用户自己定义网络传输层,实现消息的发送和接收。同时,etcd-raft定义了Storage接口,用户使用者自己定义存储层。
1. etcd-server的启动
raftNode
启动了一个包含for-select
结构的goroutine
,三个case分支的作用分别如下:
1.1 时钟驱动
通过r.ticker.C
(Time
类型的Channel
)监听时钟事件, 然后调用自身的tick
方法,raft
的时钟主要用来处理heartbeat
和election timeout
1.2 消息监听
监听消息是否到达: Ready
是raft的核心结构体,下面我们看一下Ready
各个字段的含义:
SoftState
包含了两个字段:Lead
,RaftState
。Lead表示当前Leader的ID,RaftState表示节点当前的状态(Follower,Leader,Candidate ...)
HardState
包含了:Term
,Vote
,Commit
等字段。代表了消息发送前的状态细节,比如commit索引值等,待消息发送后要做一些相应处理
ReadStates
与linearizable read
有关,后续再做解释Entries
是要在log replication
前存储到stable storage
的日志实例
Snapshot
是要存储到stable storage
的快照例CommittedEntries
要replication
给集群中其他成员的日志实例Messages
是要发送给其他成员的消息实例
MustSync
标识HardState
和Entries
是否必须写入到stable storage
2. node / raft的初始化
在etcd-raft中,有两个重要的概念:node和raft。
node是一个StateMachine,而raft代表StateMachine当前的State 。
node代表了etcd集群中的一个节点,负责时钟驱动,事件触发,IO调用;raft负责维护当前自身节点的状态:任期,Leader,日志Commit等。在 etcd
集群中,node
实例和raft
实例是一对一的关系。
2.1 node是如何启动的
通过newRaft
初始化raft
实例,并将自身状态设置为:Follower
。通过newNode
初始化node
,并通过node.run(raft)
实现node实例与raft实例的启动。通过下面的run
方法可以发现,node
和raft
之间通过Channel
进行交互。
我们看一下node.run
的核心代码:
看到这里我们应该明白了node
中的Channel
是通过tick
来驱动运作的,并且上面代码中出现了我们前面提到的 Ready
对象的构造:
3、 消息发送
heartbeat message
是由tick
触发的,heartbeat
的触发频率即为tick
的频率:
既然是tick
驱动,那我们看一下raft
在接收到tick
之后的行为。
根据raft
实例当前不同的状态,tick
会触发不同的行为:Follower
,Candidate
,PreCandidate
会触发 tickElection
,Leader
会触发tickHeartbeat
。
tickElection
会触发MessageHup
类型的消息,tickHeartbeat
会触发pb.MsgCheckQuorum
, pb.MsgBeat
类型的消息。
而上述过程中出现的 Step
方法是raft
实例消息处理的核心方法:
同样,根据当前raft
实例角色(Leader, Follower, Candidate)的不同,处理消息的类型和方式也不同,具体实现上对应不同的stepFunc
:
以stepLeader
为例,Leader处理的消息主要包括:
pb.MsgBeat, pb.MsgCheckQuorum
, pb.MsgProp
, pb.MsgReadIndex
, pb.MsgAppResp
, pb.MsgHeartbeatResp
,pb.MsgSnapStatus
,pb.MsgUnreachable
,pb.MsgTransferLeader
4、存储
etcd-raft的存储内容主要包含三部分内容:
4.1、raftLog
raft本身就像一个StateMachine
,通过log-replication
来实现成员之间状态的一致。
- Storage是一个接口,即使用者需要自己实现的存储方案,在raft/example中有一个MemoryStorage的实现,感兴趣可以自己看一下。
- unstable:已经生成或发出,尚未获得集群认可的日志
- commited:已获得集群至少二分之一成员认可的日志
- applied:已经应用到
StateMachine
的日志
4.2、binLog
- binlog 和 snapshot主要数据的迁移和恢复;
4.3、 kvStore
- 用来存储client端提交的数据,在raft/example中用hashmap代替
待续 。。。
参考链接:
https://www.cockroachlabs.com/blog/scaling-raft/www.cockroachlabs.comRecommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK