2.1 etcd源码笔记 - raft library - 整体设计

我们先了解整个 raft library 与外界是如何交互的,再从若干场景中去分析代码,理论上场景足够多,就能把代码覆盖全,跟 testcase 一个道理。

一、设计

1. 目标

一般开发一个系统或第三方库时,需要先确定对外暴露的接口,即如何与外界交互。

而确定对外暴露的接口,需要先确定其在整个系统中的职责与边界。

想象 ETCD 的核心业务功能只是简单的 KV 存储, 提供 Key 的 put、get、delete,结构如下图表示

2.1.1 - target.png

那么我们需要怎么封装 raft library 才能支撑起该功能?或者说以上哪些内容需要封装到 raft library

2. 方案

2.1.2 - design.png

ETCD如上图所示封装了 raft library

raft library 封装了整个 raft 状态机 的运作 <==> 每个使用了 raft library 的服务,成为了 raft 状态机 里的节点

需要特别注意的是,

  • raft library 只封装了图中的 应用层的交互(接口、返回)raft状态机处理(数据同步、选举、节点变更、心跳等)
  • 数据传输 则封装在另一个组件 rafthttp.Transport

一条 put(k, v) 其流程可以简单地理解为如下

  • Leader 应用层 在 put(k, v) 前, 通知其 raft library 要执行的操作
  • Leader 的 raft library 和 其他 Follower 的 raft library 协商一致
  • Leader 的 raft library 通知其应用层协商结果,应用层真正执行 put(k, v)
  • Leader 的 raft library 通知其他 Follower 的 raft library
  • 其他 Follower 的 raft library 通知其应用层,应用层真正执行 put(k, v)

二、代码分析

raft library 所有的代码都在 etcd/raft 包下

raft library 在源码文件 etcd/raft/node.go 定义了一个接口 Node, 用于表示图中所示的 raft-node-N

归纳一下,提供了以下几类方法(代码中很多bean,都直接用了protobuf,没再独立隔离一层)

1. 周期性的执行任务

Tick()

leader 需要周期性地给 follower 发心跳包

follower 需要周期性地判断一下 接收的心跳包有无超时,有则发起新一轮选举

所以不管是 leader 还是 follower,都需要周期性的执行任务

实际运行时,应用层会提供一个定时任务,然后到点自动执行该接口,

即 Node 只提供具体的任务执行,而触发时点交由应用层控制

2. 暴露给应用层的接口

//应用层调用该接口触发选举,但好像除了 testcase 没看到有嘛其他地方调用
Campaign(ctx context.Context) error
    
//应用层调用该接口向 raft 状态机 提交一条提案,比如Put、Delete
Propose(ctx context.Context, data []byte) error
    
//应用层调用该接口向 raft 状态机 提交一条配置变更提案,比如增加节点、删除节点
ProposeConfChange(ctx context.Context, cc pb.ConfChange) error

//换leader
TransferLeadership(ctx context.Context, lead, transferee uint64)
    
//应用层读取数据之前,会调用该接口,
//返回目前可以读的数据Index,即协商一致的最新提案Index
ReadIndex(ctx context.Context, rctx []byte) error

//这个我还没看咯
ApplyConfChange(cc pb.ConfChange) *pb.ConfState

3. 数据返回 ready chan

Ready() <-chan Ready

上述所列的,暴露给应用层的接口,都只有返回 error,没有返回 业务数据,这是因为采用的是异步的方式,

应用层调用接口后,直接结束,然后开始监控读取 ready chan,(这边有可能在同一个线程堵塞读取,有可能请求直接结束,然后在别的线程堵塞读取)

raft library 处理之后,再将结果写入 ready chan (ready 是一个 struct,后续再详细介绍)

4. Advance

// Advance notifies the Node that the application has saved progress up to the last Ready.
// It prepares the node to return the next available Ready.
//
// The application should generally call Advance after it applies the entries in last Ready.
//
// However, as an optimization, the application may call Advance while it is applying the
// commands. For example. when the last Ready contains a snapshot, the application might take
// a long time to apply the snapshot data. To continue receiving Ready without blocking raft
// progress, it can call Advance before finishing applying the last ready.
Advance()

应用层消费完 ready chan 数据之后,要告诉 raft 状态机 可以继续下一步的处理,需要调用该接口通知 raft 状态机

其实这么解释,还是很茫然,所谓的 “继续下一步的处理” 具体指的是神马处理,这个笔者一开始看源码的注释时也是很懵。

后续在具体的场景再详细介绍,这边为了控制篇幅,就不做过多的解释了。

5. 暴露给传输层的接口

Step(ctx context.Context, msg pb.Message) error

如上图所示,节点之间需要进行数据传输,消息接收方会调用该接口,把消息传入 raft 状态机,进行处理

这是接收方调用的接口,那发送方呢? (发送方如何告知传输方有哪些数据要传输)

这边也是通过 ready chan,实际运行时,会将要发送的消息写至 etcd/raft/raft.go:raft.msgs []pb.Message,再写到 ready chan

type Ready struct {
    ...
    // Messages specifies outbound messages to be sent AFTER Entries are
    // committed to stable storage.
    // If it contains a MsgSnap message, the application MUST report back to raft
    // when the snapshot has been received or has failed by calling ReportSnapshot.
    Messages []pb.Message
    ...
}

6. 其他辅助接口

//获取当前 raft 状态机 明细的状态
Status() Status

ReportUnreachable(id uint64)
ReportSnapshot(id uint64, status SnapshotStatus)
    
//如字面的意思,节点下线
Stop()

接口 Node 定义了以下内容

  • 暴露给应用层的API,Propose、ProposeConfChange 等
  • 暴露给应用层的数据返回,Ready()
  • 还有一些其他方法

但是没有定义与 数据传输 的交互,这个很诡异,实际上是放在了 etcd/raft/raft.go:raft.msgs []pb.Message

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容