17

etcd-raft 源码分析

 3 years ago
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.CTime类型的Channel)监听时钟事件, 然后调用自身的tick方法,raft的时钟主要用来处理heartbeatelection timeout

1.2 消息监听

监听消息是否到达: Ready是raft的核心结构体,下面我们看一下Ready 各个字段的含义:

  • SoftState包含了两个字段:Lead,RaftState。Lead表示当前Leader的ID,RaftState表示节点当前的状态(Follower,Leader,Candidate ...)
  • HardState包含了:Term,Vote,Commit等字段。代表了消息发送前的状态细节,比如commit索引值等,待消息发送后要做一些相应处理
  • ReadStateslinearizable read有关,后续再做解释
  • Entries是要在log replication前存储到stable storage的日志实例
  • Snapshot是要存储到stable storage的快照例
  • CommittedEntriesreplication给集群中其他成员的日志实例
  • Messages是要发送给其他成员的消息实例
  • MustSync标识HardStateEntries是否必须写入到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方法可以发现,noderaft之间通过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.com


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK