ETCD 六 etcd总体架构

etcd 源码结构

etcd 项目代码的目录结构:

ETCD 六 etcd总体架构_第1张图片

包名 用途
api        

protobuf定义

client/v3 客户端sdk
contrib raftexample实现
etcdctl 命令行客户端实现,用于网路的操作

etcdutl

命令行管理工具,直接操作etcd数据文件。
hack 基准测试、测试集群、k8s部署、分支管理、证书等
pkg 实用程序包的集合
raft

raft实现

server.auth 角色身份验证
server.embed 程序中绑定etcd服务器
server.etcdmain etcd服务入口
server.lease 租约
server.mvcc mvcc存储
server.proxy grpc,http,tcp代理
server.verify

分析etcd的持久状态,以发现潜在的

前后矛盾

server.wal 日志模块

etcd 整体架构

ETCD 六 etcd总体架构_第2张图片

  • 客户端层:包括 clientv3 和 etcdctl 等客户端。用户通过命令行或者客户端调用提供了 RESTful 风格的API,降低了 etcd的使用复杂度。除此之外,客户端层的负载均衡(etcd V3.4 版本的客户端默认使用的是 Round-robin,即轮询调度)和节点间故障转移等特性,提升了etcd 服务端的高可用性。需要注意的是,etcd V3.4之前版本的客户端存在负载均衡的 Bug,如果第一个节点出现异常,访问服务端时也可能会出现异常,建议进行升级。

  • API 接口层:API 接口层提供了客户端访问服务端的通信协议和接口定义,以及服务端节点之间相互通信的协议,我将会在下一讲重点讲解 etcd的通信接口。etcd 有 V3和V2 两个版本。etcd V3 使用gRPC 作为消息传输协议;对于之前的V2 版本,etcd 默认使用HTTP/1.x 协议。对于不支持 gRPC的客户端语言,etcd 提供 JSON的grpc-gateway。通过 grpc-gateway 提供 RESTful 代理,转换 HTTP/JSON 请求为 gRPC 的 Protocol Buffer 格式的消息。

  • etcd Raft 层:负责 Leader 选举和日志复制等功能,除了与本节点的 etcd Server 通信之外,还与集群中的其他 etcd 节点进行交互,实现分布式一致性数据同步的关键工作。

  • 逻辑层:etcd 的业务逻辑层,包括鉴权、租约、KVServer、MVCC 和 Compactor 压缩等核心功能特性。

  • etcd 存储:实现了快照、预写式日志 WAL(Write Ahead Log)。etcd V3 版本中,使用 BoltDB 来持久化存储集群元数据和用户写入的数据。

 etcd 交互总览

通过命令行工具 etcdctl 写入键值对:

etcdctl --endpoints http://127.0.0.1:2379 put foo bar

下图中展示了 etcd 处理一个客户端请求涉及的模块和流程:

ETCD 六 etcd总体架构_第3张图片 

从上至下依次为客户端 → API 接口层 → etcd Server → etcd raft 算法库。我们根据请求处理的过程,将 etcd Server 和 etcd raft 算法库单独说明。

  • etcd Server:接收客户端的请求,在上述的etcd 项目代码中对应etcdserver 包。请求到达 etcd Server 之后,经过 KVServer 拦截,实现诸如日志、Metrics 监控、请求校验等功能。etcd Server 中的raft模块,用于与 etcd-raft 库进行通信。applierV3 模块封装了 etcd V3 版本的数据存储;WAL 用于写数据日志,WAL中保存了任期号、投票信息、已提交索引、提案内容等,etcd 根据 WAL 中的内容在启动时恢复,以此实现集群的数据一致性。

  • etcdraft:etcd 的raft 库。raftLog 用于管理 raft 协议中单个节点的日志,都处于内存中。raftLog 中还有两种结构体 unstable和storage,unsable 中存储不稳定的数据,表示还没有 commit,而 storage中都是已经被 commit 了的数据。这两种结构体分别用于不同步骤的存储,我们将在下面的交互流程中介绍。除此之外,raft 库更重要的是负责与集群中的其他 etcd Server进行交互,实现分布式一致性。

在上图中,客户端请求与 etcd 集群交互包括如下两个步骤:

  • 首先是写数据到 etcd 节点中;

  • 其次是当前的 etcd 节点与集群中的其他 etcd 节点之间进行通信,确认存储数据成功之后回复客户端。

请求流程可划分为以下的子步骤:

ETCD 六 etcd总体架构_第4张图片

  • 客户端通过负载均衡算法选择一个 etcd 节点,发起 gRPC 调用;

  • etcd Server 收到客户端请求;

  • 经过 gRPC 拦截、Quota 校验,Quota 模块用于校验 etcd db 文件大小是否超过了配额;

  • 接着 KVServer 模块将请求发送给本模块中的raft,这里负责与 etcd raft模块进行通信;

  • 发起一个提案,命令为put foo bar,即使用put 方法将 foo 更新为 bar;

  • 在raft 中会将数据封装成 raft 日志的形式提交给 raft模块;

  • raft模块会首先保存到 raftLog 的 unstable 存储部分;

  • raft模块通过raft 协议与集群中其他 etcd 节点进行交互。

需要注意的是,在 raft 协议中写入数据的 etcd 必定是 leader 节点,如果客户端提交数据到非 leader 节点时,该节点需要将请求转发到 etcd leader 节点处理。

我们继续来看相应的应答步骤,流程如下:

ETCD 六 etcd总体架构_第5张图片

  • 提案通过 RaftHTTP 网络模块转发,集群中的其他节点接收到该提案;

  • 在收到提案之后,集群中其他节点向 leader 节点应答“我已经接收这条日志数据”;

  • Leader收到应答之后,统计应答的数量,当满足超过集群半数以上节点,应答接收成功;

  • etcd raft算法模块构造 Ready 结构体,用来通知 etcd Server 模块,该日志数据已经被 commit;

  • etcd Server 中的 raft 模块(交互图中有标识),收到 Ready 消息后,会将这条日志数据写入到 WAL 模块中;

  • 正式通知 etcd Server 该提案已经被 commit;

  • etcd Server 调用 applierV3 模块,将日志写入持久化存储中;

  • etcd Server 应答客户端该数据写入成功;

  • etcd Server 调用 etcd raft 库,将这条日志写入到 raftLog 模块中的 storage。

上述过程中,提案经过网络转发,当多数etcd 节点持久化日志数据成功并进行应答,提案的状态会变成已提交

在应答某条日志数据是否已经 commit 时,为什么 etcd raft 模块首先写入到 WAL 模块中?这是因为该过程仅仅添加一条日志,一方面开销小,速度会很快;另一方面,如果在后面 applierV3 写入失败,etcd 服务端在重启的时候也可以根据 WAL 模块中的日志数据进行恢复。etcd Server 从 raft 模块获取已提交的日志条目,由 applierV3 模块通过 MVCC 模块执行提案内容,更新状态机。

整个过程中,etcd raft 模块中的 raftLog 数据在内存中存储,在服务重启后失效;客户端请求的数据则被持久化保存到 WAL 和 applierV3 中,不会在重启之后丢失。

 

 

 

你可能感兴趣的:(etcd,etcd,架构,数据库)