etcd 实战课
唐聪
腾讯云资深工程师,etcd 活跃贡献者
28449 人已学习
新⼈⾸单¥59
登录后,你可以任选3讲全文学习
课程目录
已完结/共 28 讲
开篇词 (1讲)
etcd 实战课
15
15
1.0x
00:00/00:00
登录|注册

02 | 基础架构:etcd一个读请求是如何执行的?

boltdb
buffer
treeIndex
串行读与线性读
拦截器
负载均衡算法
clientv3库
etcdctl工作原理
boltdb模块
快照(Snapshot)模块
预写日志(WAL)模块
Compactor压缩模块
Lease租约模块
Auth鉴权模块
MVCC模块
KVServer模块
ReadIndex
日志复制
Leader选举
server节点之间的通信协议
client访问server
client v3
client v2
MVCC
线性读之ReadIndex
KVServer
client
存储层
功能逻辑层
Raft算法层
API网络层
Client层
读请求执行流程
etcd v3的基础架构
etcd基础架构

该思维导图由 AI 生成,仅供参考

你好,我是唐聪。
在上一讲中,我和你分享了 etcd 的前世今生,同时也为你重点介绍了 etcd v2 的不足之处,以及我们现在广泛使用 etcd v3 的原因。
今天,我想跟你介绍一下 etcd v3 的基础架构,让你从整体上对 etcd 有一个初步的了解,心中能构筑起一幅 etcd 模块全景图。这样,在你遇到诸如“Kubernetes 在执行 kubectl get pod 时,etcd 如何获取到最新的数据返回给 APIServer?”等流程架构问题时,就能知道各个模块由上至下是如何紧密协作的。
即便是遇到请求报错,你也能通过顶层的模块全景图,推测出请求流程究竟在什么模块出现了问题。

基础架构

下面是一张 etcd 的简要基础架构图,我们先从宏观上了解一下 etcd 都有哪些功能模块。
你可以看到,按照分层模型,etcd 可分为 Client 层、API 网络层、Raft 算法层、逻辑层和存储层。这些层的功能如下:
Client 层:Client 层包括 client v2 和 v3 两个大版本 API 客户端库,提供了简洁易用的 API,同时支持负载均衡、节点间故障自动转移,可极大降低业务使用 etcd 复杂度,提升开发效率、服务可用性。
API 网络层:API 网络层主要包括 client 访问 server 和 server 节点之间的通信协议。一方面,client 访问 etcd server 的 API 分为 v2 和 v3 两个大版本。v2 API 使用 HTTP/1.x 协议,v3 API 使用 gRPC 协议。同时 v3 通过 etcd grpc-gateway 组件也支持 HTTP/1.x 协议,便于各种语言的服务调用。另一方面,server 之间通信协议,是指节点间通过 Raft 算法实现数据复制和 Leader 选举等功能时使用的 HTTP 协议。
Raft 算法层:Raft 算法层实现了 Leader 选举、日志复制、ReadIndex 等核心算法特性,用于保障 etcd 多个节点间的数据一致性、提升服务可用性等,是 etcd 的基石和亮点。
功能逻辑层:etcd 核心特性实现层,如典型的 KVServer 模块、MVCC 模块、Auth 鉴权模块、Lease 租约模块、Compactor 压缩模块等,其中 MVCC 模块主要由 treeIndex 模块和 boltdb 模块组成。
存储层:存储层包含预写日志 (WAL) 模块、快照 (Snapshot) 模块、boltdb 模块。其中 WAL 可保障 etcd crash 后数据不丢失,boltdb 则保存了集群元数据和用户写入的数据。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

etcd v3的基础架构包括Client层、API网络层、Raft算法层、逻辑层和存储层。在读请求执行过程中,etcdctl首先解析命令参数,然后选择合适的etcd server节点进行负载均衡,使用基于HTTP/2的gRPC协议与server通信。接着进入KVServer模块,其中拦截器提供了请求前后的hook能力,实现了诸如debug日志、metrics统计、对etcd Learner节点请求接口和参数限制等特性。最后,handler将请求转发到对应的实现,通过调用KVServer模块的Range接口获取数据。整体而言,etcd v3的基础架构通过各个模块的协作,实现了高性能、低延迟的读请求执行流程。 在多节点etcd集群中,各个节点的状态机数据一致性存在差异。etcd提供了串行读和线性读两种模式,分别适用于对数据一致性要求不高和要求高的场景。线性读通过ReadIndex机制保证数据一致性,确保最新的数据已经应用到状态机中。此外,MVCC模块解决了etcd v2不支持保存key的历史版本、不支持多key事务等问题,通过内存树形索引模块和嵌入式的KV持久化存储库boltdb实现多版本并发控制。整个读请求执行流程经过多个模块的协作,保证了数据一致性和高性能。 总体而言,etcd v3的读请求执行流程经历了负载均衡、拦截器处理、Raft协议通信、数据一致性保证和多版本并发控制等环节,为用户提供了高性能、低延迟的读取体验。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《etcd 实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(68)

  • 最新
  • 精选
  • hiroshi
    老师,readIndex 需要请求 leader,那为啥不直接让 leader 返回读请求的结果,而要等待自己的进度赶上 leader?

    作者回复: 非常好的问题,我个人认为主要还是性能因素,我记得etcd v2早期的时候如果你指定线性读/共识读,它就是直接转发给leader的。后来在etcd v3.0中实现了raft log read但是要走一遍raft log,读涉及到磁盘IO,v3.1中引入了readIndex机制,它是非常轻量级的,开销较小,相比各个follower都转发给leader会导致leader负载较高,特别是expensive request场景,性能会急剧下降,leader的内存、cpu、网络带宽资源都很容易耗尽,readIndex机制的引入,使得每个follower节点都可以处理读请求,极大扩展提升了写性能。

    2021-02-16
    8
    69
  • 姜姜
    老师,文中有些地方不太明白: 1, KVServer中的拦截器 我认为它只是作为一个辅助的功能吧,用于实现一些观测功能。但对于一个普通的读请求,是否必须通过拦截器才能完成读取数据的操作? 2, 文中“handler 首先会将上面描述的一系列拦截器串联成一个执行” 这段话中,拦截器是一系列的,一系列是指会有多个拦截器吗?难道不是一个请求只注册一个拦截器吗,还能注册多个?为什么要注册多个? “串联成一个执行”,如何串联成一个?将多个拦截器串联成一个拦截器? 3, 串行读与线性读 这里我理解串行读是“非强一致性读”,线性读是“强一致性读”,对吗? 而且这里的“串行”总让我想到“并行/串行”的概念,不知有关系吗? 4, ReadIndex,committed index,applied index 这几种索引底层实现是一样的吗,它们的数据结构是怎样的?是对同一份数据,分别建立不同的索引?又为什么建立这么多种索引? 5,版本号 您说是一个递增的全局ID, revision{2, 0},ID指的是2还是0? 版本号的格式是怎样的,另一个数字代表什么? 6, bucket 请问一个 bucket 相当于一整个 B+ tree 索引树吗?还是相当于 B+ tree 中一个节点?

    作者回复: 谢谢你的提问,我先简单快速回答下,后面不清楚的再写答疑文章深入解答 问题1和2是gRPC拦截器相关知识我推荐你看下这篇文章https://zhuanlan.zhihu.com/p/80023990 问题3你理解串行读是“非强一致性读”,线性读是“强一致性读”没问题,至于串行含义并非你想的那样,你可以参考下维基百科的定义, 09事务篇我也会介绍事务隔离中的串行化 https://en.wikipedia.org/wiki/Serializability 问题4 建议先去阅读下04 raft篇,它本值上就是一个uint64的索引,表示日志条目序号 问题5,{2,0}={major,sub} 2是etcd mvcc事务版本号全局递增,0是事务内子版本号随修改操作递增(比如一个txn事务中多个put/delete操作,其会从0递增),07 mvcc会详细介绍 问题6,一个bucket对应一个颗B+tree

    2021-01-26
    6
    29
  • 小军
    请问老师,当Readindex结束并等待本节点的状态机apply的时候,key又被最新的更新请求给更新了怎么办,这个时候读取到的value是不是又是旧值了

    作者回复: 线性读,读出来的值实际上是你发出读请求时间点的集群最新共识数据,在你读请求发出后,若耗时一定时间还未完成,在这过程中leader又收到了写请求更新了它, 的确你原来读出来的值相比最新的集群共识就是旧的,在实际应用中,我们一般会通过增加版本号检测识别此类问题,后面事务篇会详细和你介绍

    2021-01-26
    24
  • chapin
    没有基础,学习这个,可能会比较吃力。

    作者回复: 没关系的,可以先大概看一篇,了解整个流程,不懂什么地方可以等学完后面后,回过头来再看就非常亲切了,后面每节中都有etcd特性体验案例,建议你跟着我一起实际操作下,比如02你就先准备好环境,能用goreman快速启一个多节点集群,也可以自己直接二进制启动一个单节点集群,然后体验一下get,put命令,随着后面的学习你会越来越了解etcd

    2021-01-26
    11
  • 站在树上的松鼠
    老师,下面这句话没有理解到,麻烦解答下呢,谢谢! 在client 3.4之前的版本中,负载均衡算法有一个严重的Bug:如果第一个节点异常了,可能会导致你的client访问etcd server异常。 (1)这里第一个节点怎么理解呢? 是指的负载均衡刚好选中的那个etcd server节点异常吗? (2)如果访问的节点异常了,是client库中会做重试机制,还是业务代码需要做重试呢?

    作者回复: 感谢超凡帮忙解答第一点,第二点取决于rpc方法,range clientv3库有重试策略,参考一下这个文件clientv3/retry.go

    2021-01-22
    4
    7
  • jeffery
    干货太多需要慢慢消化!老师能把课程代码放到github上吗……谢谢老师

    作者回复: 嗯,不清楚的地方不要急,后面的每节会帮助你一个个解开疑问

    2021-01-22
    7
  • Want less
    当收到一个线性读请求时,它首先会从 Leader 获取集群最新的已提交的日志索引 (committed index)。 所有的client请求不是应该都通过leader下发至follower吗?

    作者回复: 不是的哈,follower节点也可以处理读请求的,只是线性读时需要向leader发送readindex消息,然后确保本节点数据是最新的

    2021-01-27
    4
    5
  • Alery
    请教一个问题,在treeIndex中查询key对应的版本号,这里是会返回当前key的所有版本号吗?

    作者回复: 嗯,每个key在treeIndex中有一个对应的数据结构keyIndex,它保存了所有版本号(若未压缩),07讲mvcc将详细介绍

    2021-01-26
    5
  • yayiyaya
    问答: etcd 在执行读请求过程中涉及磁盘 IO 吗? 答: 涉及到磁盘, 当读请求从treeIndex获取到用户的 key 和相关版本号信息后,去查询value值时, 没有命中 buffer, 会从boltdb获取数据, 这个时候就涉及到了磁盘。

    作者回复: etcd启动的时候通过mmap将db文件映射到内存,会告诉内核预读文件,下一讲给了参考答案

    2021-02-04
    2
    4
  • 于途
    如果你的 client 版本 <= 3.3,那么当你配置多个 endpoint 时,负载均衡算法仅会从中选择一个 IP 并创建一个连接(Pinned endpoint) 请问,此句提到的负载均衡算法是否等同:随机选中某个IP?

    作者回复: 嗯,可以理解为随机,首先它会尝试连接所有etcd节点,连接建立后选择一个固定的长连接,其他关闭

    2021-01-22
    4
收起评论
显示
设置
留言
68
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部