etcd 笔记(07)— 键值对读写操作过程

1. 读写总体概述

etcd 各个模块交互的总览,如下图所示:
总览
总体上的请求流程从上至下依次为客户端 → API 接口层 → etcd Serveretcd raft 算法库。

  • 读请求

客户端通过负载均衡选择一个 etcd 节点发出读请求,API 接口层提供了 Range RPC 方法,etcd 服务端拦截到 gRPC 读请求后,调用相应的处理器处理请求。

  • 写请求

客户端通过负载均衡选择一个 etcd 节点发起写请求,etcd 服务端拦截到 gRPC 写请求,涉及一些校验和监控,之后 KVServerraft 模块发起提案,内容即为写入数据的命令。经过网络转发,当集群中的多数节点达成一致并持久化数据后,状态变更且 MVCC 模块执行提案内容。

2. 读操作整体流程

将整个读操作划分成如下几个步骤:

  • etcdctl 会创建一个 clientv3 库对象,选取一个合适的 etcd 节点;

  • 调用 KVServer 模块的 Range RPC 方法,发送请求;

  • 拦截器拦截,主要做一些校验和监控;

  • 调用 KVServer 模块的 Range 接口获取数据;

接着就进入了读请求的核心步骤,会经过线性读 ReadIndex 模块、MVCC(包含 treeIndexBlotDB)模块。

线性读是相对串行读来讲的概念。集群模式下会有多个 etcd 节点,不同节点之间可能存在一致性问题,串行读直接返回状态数据,不需要与集群中其他节点交互。这种方式速度快,开销小,但是会存在数据不一致的情况。

线性读则需要集群成员之间达成共识,存在开销,响应速度相对慢。但是能够保证数据的一致性,etcd 默认读模式是线性读。

继续往下,看看如何读取 etcd 中的数据。etcd 中查询请求,查询单个键或者一组键,以及查询数量,到了底层实际都会调用 Range keys 方法。

Range 请求的结构图如下所示:
ranges
从上至下,查询键值对的流程包括:

  • treeIndex 中根据键利用 BTree 快速查询该键对应的索引项 keyIndex,索引项中包含 Revision

  • 根据查询到的版本号信息 Revision,在 Backend 的缓存 Buffer 中利用二分法查找,如果命中则直接返回;

  • 若缓存中不符合条件,在 BlotDB 中查找(基于 BlotDB 的索引),查询之后返回键值对信息。

图中 ReadTxBatchTx 是两个接口,用于读写请求。在创建 Backend 结构体时,默认也会创建 readTxbatchTxreadTx 实现了 ReadTx ,负责处理只读请求;batchTx 实现了 BatchTx 接口,负责处理读写请求。

总结客户端发起读请求之后的处理流程,如下图所示:
读操作

  • 客户端发起请求之后,clientv3 首先会根据负载均衡算法选择一个合适的 etcd 节点,接着调用 KVServer 模块对应的 RPC 接口,发起 Range 请求的 gRPC 远程调用;

  • gRPC Server 上注册的拦截器拦截到 Range 请求,实现 Metrics 统计、日志记录等功能;

  • 然后进入读的主要过程,etcd 模式实现了线性读,使得任何客户端通过线性读都能及时访问到键值对的更新;

  • 线性读获取到 Leader 已提交日志索引构造的最新 ReadState 对象,实现本节点状态机的同步;

  • 接着就是调用 MVCC 模块,根据 treeIndex 模块 B-tree 快速查找 key 对应的版本号;

  • 通过获取的版本号作为 key,查询存储在 boltdb 中的键值对;

3. 写操作整体流程

将整个写操作划分成如下几个步骤:

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

  • etcd Server 收到客户端请求;

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

  • 接着 KVServer 模块将请求发送给本模块中的 raft,这里负责与 etcd raft 模块进行通信,发起一个提案,命令为 put foo bar,即使用 put 方法将 foo 更新为 bar

  • 提案经过转发之后,半数节点成功持久化;

  • MVCC 模块更新状态机;

put 接口的执行过程:
put
调用 putetcd 写入数据时,首先会使用传入的键构建 keyIndex 结构体,基于 currentRevision 自增生成新的 Revision{1,0} ,并从 treeIndex 中获取相关版本 Revision 等信息;写事务提交之后,将本次写操作的缓存 buffer 合并(merge)到读缓存上(图中 ReadTx 中的缓存)。

revision{1,0} 是生成的全局版本号,作为 BoltDBkey,经过序列化包括 key 名称、key 创建时的版本号(create_revision)、value 值和租约等信息为二进制数据之后,将填充到 BoltDBvalue 中,同时将该键和 Revision 等信息存储到 Btree

根据 etcd 读写流程图,可以知道读写操作依赖 MVCC 模块的 treeIndexBoltDBtreeIndex 用来保存键的历史版本号信息,而 BoltDB 用来保存 etcd 的键值对数据。通过这两个模块之间的协作,实现了 etcd 数据的读取和存储。

写请求的处理流程,如下图所示:
写

  • 客户端发送写请求,通过负载均衡算法选取合适的 etcd 节点,发起 gRPC 调用。
  • etcd server 的 gRPC Server 收到这个请求,经过 gRPC 拦截器拦截,实现 Metrics 统计和日志记录等功能。
  • Quota 模块配额检查 db 的大小,如果超过会报etcdserver: mvcc: database space exceeded的告警,通过 Raft 日志同步给集群中的节点 db 空间不足,同时告警也会持久化到 db 中。etcd 服务端拒绝写入,对外提供只读的功能。
  • 配额检查通过,KVServer 模块经过限速、鉴权、包大小判断之后,生成唯一的编号,这时才会将写请求封装为提案消息,提交给 Raft 模块。
  • 写请求的提案只能由 Leader 处理,获取到 Raft 模块的日志条目之后,Leader 会广播提案内容。WAL 模块完成 Raft 日志条目内容封装,当集群大多数节点完成日志条目的持久化,即将提案的状态变更为已提交,可以执行提案内容。
  • Apply 模块用于执行提案,首先会判断该提案是否被执行过,如果已经执行,则直接返回结束;未执行过的情况下,将会进入 MVCC 模块执行持久化提案内容的操作。
  • MVCC 模块中的 treeIndex 保存了 key 的历史版本号信息,treeIndex 使用 B-tree 结构维护了 key 对应的版本信息,包含了全局版本号、修改次数等属性。版本号代表着 etcd 中的逻辑时钟,启动时默认的版本号为 1。键值对的修改、写入和删除都会使得版本号全局单调递增。写事务在执行时,首先根据写入的 key 获取或者更新索引,如果不存在该 key,则会给予当前最大的 currentRevision 自增得到 revision;否则直接根据 key 获取 revision。
  • 根据从 treeIndex 中获取到 revision 、修改次数等属性,以及 put 请求传递的 key-value 信息,作为写入到 boltdb 的 value,而将 revision 作为写入到 boltdb 的 key。同时为了读请求能够获取最新的数据,etcd 在写入 boltdb 时也会同步数据到 buffer。因此上文介绍 etcd 读请求的过程时,会优先从 buffer 中读取,读取不到的情况下才会从 boltdb 读取,以此来保证一致性和性能。为了提高吞吐量,此时提案数据并未提交保存到 db 文件,而是由 backend 异步 goroutine 定时将批量事务提交。
  • Server 通过调用网络层接口返回结果给客户端。

总的来说,这个过程为客户端发起写请求,由 Leader 节点处理,经过拦截器、Quota 配额检查之后,KVServer 提交一个写请求的提案给 Raft 一致性模块,经过 RaftHTTP 网络转发,集群中的其他节点半数以上持久化成功日志条目,提案的状态将会变成已提交。接着 Apply 通过 MVCC 的 treeIndex、boltdb 执行提案内容,成功之后更新状态机。

原文:
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=613#/detail/pc?id=6411

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值