欢迎光临散文网 会员登陆 & 注册

ETCD 深入(一):揭开etcd神秘面纱-源码实现剖析

2022-02-20 16:12 作者:九霄如歌  | 我要投稿

文本为 etcd 深入系列文章的首篇。etcd 是非常优秀的开源项目,深入系列的目标是通过阅读 etcd 源码,跟随 etcd 的实现思路,学习如何实现数据强一致性、raft 共识算法、多版本并发控制等分布式系统原理,同时也关注 etcd 在 golang 程序设计上的良好实践。


本篇文章主要介绍 etcd 实现的基本架构、源码组织结构,以及基本的读写流程。每部分的细化研读会放在之后的系列文章中。

etcd 总体架构

etcd 中比较核心的四个模块就是:etcd server、raft 共识算法模块、WAL 持久化日志模块 、MVCC 状态机存储和多版本控制模块 。它们之间互相交互完成了读写流程。


grpc server 是最上层处理客户端请求层,主要处理了 HTTP/ grpc 等协议,本文先不做过多介绍。


一次完整的读写流程

写请求


  1. leader 节点收到写请求,会发送给 raft 模块

  1. raft 模块会将写请求同步给其他 follower 节点;与此同时 leader 节点会将写请求日志写入自己的 WAL;

  2. follower 节点回复是否接受本次写请求;

  1. 超过半数  (n/2+1)的 follower 节点同意写入,则此条数据认为可以被 commit 。raft 模块会通知 etcd server 模块此条数据已经被 commit,可以进行 Apply;

  2. 各节点的  etcd server  向 MVCC 状态机存储模块发起 Apply 请求,写入后端存储,完成本节点的写请求数据版本持久化。

     不同节点间的 raft 模块会相互通信,保持对 raft log 状态的一致性,即一旦认为此条写请求可以开始进行 Apply,此时各节点的  raft 模块都会各自通知自己的 etcd server 进行 Apply;但是不同节点的 Apply 速度可能会有差异。那么 client 端会因此读不到最新的数据吗?并不会,因为 etcd 的 ReadIndex 机制保证了读请求的线性一致性,下文会详细描述。

  1. 当 client 所连接的节点完成 Apply 操作后,会返回 client 端本次写请求成功。

读请求


  1. etcd 任意节点的 etcd server 收到读请求。

  2. 判断读请求类型,如果是串行化读,则直接进入 Apply 流程。即直接从本节点的 MVCC 后端存储中读取数据。(不一定是最新值);

  1. 如果是线性一致性读,则进去 Raft 模块;

  2. Raft 模块向 leader 节点发起 ReadIndex 请求,获取目前已经提交的最新 Index ;()

  1. 然后节点会等待本地的 AppliedIndex 大于或等于 ReadIndex 请求返回的 CommittedIndex, 才进入 Apply 流程;

  2. Apply 流程:通过 Key 名从 TreeIndex 中获取对用的 Revision,再通过 Revision 从 BoltDB 中获取对应的 Key Value。

其中第5点是最重要的,它表明节点 Apply 到 CommittedIndex 之后的状态都能使读请求满足线性一致性。

 


etcd Server 模块

TBD

Raft 模块 :

etcd 是一个强一致性的分布式 kv 存储系统。强一致性指的是一旦一个数据写入成功后,从任意节点读取出来的数据都是最新值,保证不会出现读失败或者读出来旧值的情况。那么 etcd 到底是如何实现强一致性的呢?其中主要的 “功劳” 都是 raft 共识算法保证的。可见 raft 模块也是 etcd 最核心的模块。

etcd 依赖 raft 算法实现 leader 选举、数据一致性。共识算法能保证多个节点对某个对象的状态是一致的,在 Raft 算法中即保证了多个节点对 Raft Log 的状态是一致的。

复制状态机



Replicated state machine architecture. The consensus algorithm manages a replicated log containing state machine commands from clients. The state machines process identical sequences of commands from the logs, so they produce the same outputs.


节点选举、共识算法与数据一致性


TBD


线性一致性读

上文提到,etcd 如何通过 readIndex 实现强一致性? 

通过分析一个完整的读操作可以看出,etcd 为了达成线性一致性读需要满足下面两个条件:

由 leader 来处理只读请求:如果 Follower 收到读请求,需要将请求转发给 leader 处理;

确保 leader 的有效性:leader 需要向集群发送一次广播,如果能收到大多数节点的回应,说明 leader 节点的身份是有效的,这一过程是为了确保 leader 节点的数据都是最新的。


在 etcd 中实现读请求的线性一致性必须要经过 Raft 一致性协议,这种方式必须要付出一些性能和时延的代价。如果对数据一致性没有特别强的要求,可以在客户端将一致性模式配置成串行化(Serializable)。Serializable 模式的只读请求可以由任何一个 etcd 成员提供,但是 etcd 可能会因此读到过期的数据。


优化:LeaseRead



优化:Wait Free


Storage 后端存储

WAL 模块:数据持久化

TBD

MVCC 模块:状态机存储

TBD


BoltDB:索引存储

TBD

总结

TBD



参考

  1. https://github.com/etcd-io

  2. In Search of an Understandable Consensus Algorithm

  1. 分布式键值存储 etcd 原理与实现

  2. 线性一致性和 Raft

  1. 高可用分布式存储 etcd 的实现原理


ETCD 深入(一):揭开etcd神秘面纱-源码实现剖析的评论 (共 条)

分享到微博请遵守国家法律