etcd 笔记(05)— etcd 代码结构、各模块功能、整体架构、各模块之间的交互、请求和应答流程

1. etcd 项目结构和功能

etcd 项目代码的目录结构如下:

$ tree
├── auth
├── build
├── client
├── clientv3
├── contrib
├── embed
├── etcdctl
├── etcdmain
├── etcdserver
├── functional
├── hack
├── integration
├── lease
├── logos
├── mvcc
├── pkg
├── proxy
├── raft
├── scripts
├── security
├── tests
├── tools
├── vendor
├── version
└── wal

每个模块的功能如下:
功能
etcd 核心的模块有 leasemvccraftetcdserver,其余都是辅助的功能。其中 etcdserver 是其他模块的整合。

2. etcd 整体架构

etcd 整体架构图如下:
架构

2.1 客户端层

包括 clientv3etcdctl 等客户端。用户通过命令行或者客户端调用提供了 RESTful 风格的 API,降低了 etcd 的使用复杂度。

除此之外,客户端层的负载均衡(etcd V3.4 版本的客户端默认使用的是 Round-robin,即轮询调度)和节点间故障转移等特性,提升了 etcd 服务端的高可用性。需要注意的是,etcd V3.4 之前版本的客户端存在负载均衡的 Bug,如果第一个节点出现异常,访问服务端时也可能会出现异常,建议进行升级。

2.2 API 接口层

API 接口层提供了客户端访问服务端的通信协议和接口定义,以及服务端节点之间相互通信的协议。

etcdV3V2 两个版本。

  • etcd V3 使用 gRPC 作为消息传输协议;
  • etcd V2 默认使用 HTTP/1.x 协议。

对于不支持 gRPC 的客户端语言,etcd 提供 JSONgrpc-gateway 。通过 grpc-gateway 提供 RESTful代理,转换 HTTP/JSON 请求为 gRPCProtocol Buffer 格式的消息。

2.3 etcd Raft 层

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

2.4 逻辑层

etcd 的业务逻辑层,包括鉴权、租约、KVServerMVCCCompactor 压缩等核心功能特性。

2.5 etcd 存储

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

3. 各个模块之间的交互

下图中展示了 etcd 处理一个客户端请求涉及的模块和流程。
交互流程
从上至下依次为 客户端 → API 接口层 → etcd Serveretcd raft 算法库。我们根据请求处理的过程,将 etcd Serveretcd raft 算法库单独说明。

3.1 etcd Server

接收客户端的请求,在上述的 etcd 项目代码中对应 etcdserver 包。请求到达 etcd Server 之后,经过 KVServer 拦截,实现诸如日志、Metrics 监控、请求校验等功能。

etcd Server 中的 raft 模块,用于与 etcd-raft 库进行通信。

applierV3 模块封装了 etcd V3 版本的数据存储;WAL 用于写数据日志,WAL 中保存了任期号、投票信息、已提交索引、提案内容等,etcd 根据 WAL 中的内容在启动时恢复,以此实现集群的数据一致性。

3.2 etcdraft

etcdraft 库。raftLog 用于管理 raft 协议中单个节点的日志,都处于内存中。

raftLog 中还有两种结构体 unstablestorage,这两种结构体分别用于不同步骤的存储,区别如下

  • unsable 中存储不稳定的数据,表示还没有 commit
  • storage 中都是已经被 commit 了的数据;

除此之外,raft 库更重要的是负责与集群中的其他 etcd Server 进行交互,实现分布式一致性。

3.3 交互细节

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

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

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

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

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

  2. etcd Server 收到客户端请求;

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

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

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

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

  7. raft 模块会首先保存到 raftLogunstable 存储部分;

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

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

响应流程的步骤如下:
响应

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

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

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

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

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

  6. 正式通知 etcd Server 该提案已经被 commit

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

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

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

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

在应答某条日志数据是否已经 commit 时,为什么 etcd raft 模块首先写入到 WAL 模块中?

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

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

参考:
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=613#/detail/pc?id=6403

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值