新老朋友好久不见,我是大彬,本来计划在7月份连续发几篇垃圾回收的文章,结果一到下半年,事情多了不少,周末就只能看看源码,写写笔记了,要整理成文章,就还有点距离了,关于GC的文章各位大佬再等等吧。
为了证明本号还活着,江湖救急,发几篇其他的笔记吧,这篇文章就介绍etcd/raft的宏观结构,目录先走起。
序言raft服务etcd raftraft应用架构http APIkv storeraftNoderaftNode与raft交互raft架构node结构体raft结构体几个存储相关的概念一个写请求的处理过程总结
Etcd提供了一个样例contrib/raftexample,用来展示如何使用etcd raft,这篇文章通过raftexample介绍如何使用etcd raft。
raftexample是一个分布式KV数据库,客户端可以向集群的节点发送写数据和读数据,以及修改集群配置的请求,它使用etcd raft保持各集群之间数据的一致性。
etcd raft实现了raft论文的核心,所有的IO(磁盘存储、网络通信)它都没有实现,它做了解耦。
它是一个状态机,有数据作为输入,经过当前状态和输入,得到确定性的输出,即每个节点上都是一样的。
raft集群会由多个节点组成,客户端的请求发送给raft leader,再由raft leader通过网络通信在集群之中对请求达成共识。
集群中的每个节点从架构上都可以分为两层:
共识层由etcd raft负责,应用层要负责业务逻辑,数据存储和网络通信不需要应用层实现,而是由不同的模块负责,应用层负责起衔接存储存储和网络通信即可。
每个节点都会启动一个http API用来接受客户端请求,它只是接收请求,不对请求做处理。它会把客户端的写入请求PUT和查询请求GET都交给kv store。
对于修改raft集群配置请求,它会生成ConfChange
交给raftNode。
一个kv数据库服务,它保存有一个kv db,用来存储用户数据。
raftNode用来跟etcd raft交互,他需要:
对于写请求,它会把请求数据编码后发送给etcd raft,etcd raft会把写请求封装成raft的Propose消息MsgProp
,编码后的数据成为log Entry。因为raft并不关心具体的请求内容,它只需要保证每个集群节点在相同的log index拥有相同的log Entry,即请求即可。
raftNode还会启动1个http server,用来集群节点之间的通信,传递raft消息,让集群节点达成共识。它与http api是不同的,http api用来接收用户请求。
raft模块内部定义了一个Node
接口,它代表了raft集群中的一个raft节点,它是应用层跟共识层交互的接口。
其中有几个与数据传递相关函数的是:
还有一个ApplyConfChange函数,当Ready结构体中包含修改raft集群配置的log entry时,应用层会调用此函数,把配置应用到raft。
瞄完raft应用架构,可以从宏观角度看一下raft是如何跟应用层对接的。
raft包内部有2个很重要的结构体:node和raft。
node结构体(后续称为raft.node)实现了Node
接口,负责跟应用层对接,raft.node有个goroutine持续运行,应用层raftNode也有goroutine持续运行,raftNode调用raft.node的函数,每个函数都有对应的一个channel,用来把raftNode要传递给raft的数据,发送给raft.node。比如Propose函数的通道是proc,Step函数的通道是recvc。
raft结构体(后续称为raft.raft)是raft算法的主要实现。
raft.node把输入推给raft.raft,raft.raft根据输入和当前的状态数据生成输出,输出临时保存在raft内,raft.node会检查raft.raft是否有输出,如果有输出数据,就把输出生成Ready结构体,并传递给应用层。
raft.raft应用层有一个storage,存放的是当前的状态数据,包含了保存在内存中的log entry,但这个storage并不是raft.raft的,是应用层的,raft.raft只从中读取数据,log entry的写入由应用层负责。
WAL是Write Ahead Logs的缩写,存储的是log entry记录,即所有写请求的记录。
storage也是存的log entry,只不过是保存在内存中的。
kv db是保存了所有数据的最新值,而log entry是修改数据值的操作记录。
log entry在集群节点之间达成共识之后,log entry会写入WAL文件,也会写入storage,然后会被应用到kv store中,改变kv db中的数据。
Snapshot是kv db在某个log entry被应用后生成的快照,可以根据快照快速回复kv db,而无需从所有的历史log entry依次应用,恢复kv db。
有了上面架构层面的了解,我们从宏观的角度看一下一个写请求被处理的过程。
本文从宏观角度介绍了:
这是Go语言充电站的第 29 期分享。
Copyright© 2013-2019