etcd读写请求的执行过程

news2024/11/28 13:41:39

etcd读请求如何执行

首先,etcdctl 会对命令中的参数进行解析。在解析完请求中的参数后,etcdctl 会创建一个 clientv3 库对象通过gRPC API来访问 etcd server。对应流程一。

然后通过负载均衡算法选择一个etcd server节点,然后调用 etcd server 的 KVServer 模块的 Range RPC 方法,把请求发送给 etcd server。在 etcd 3.4 中,clientv3 库采用的负载均衡算法为 Round-robin,对应流程二。

KVServer 收到 client 的 Range RPC 请求后,首先会根据 ServiceName 和 RPC Method 将请求转发到对应的 handler 实现,handler 首先会将metrics、日志、请求行为检查等一系列拦截器串联后执行。之后就进入核心的读流程,对应架构图中的流程三和四,这里首先介绍下串行读和线性读。

串行读

etcd 的串行 (Serializable) 读是直接读状态机数据返回、无需通过 Raft 协议与集群进行交互的模式。具有低延时、高吞吐量的特点,适合对数据一致性要求不高的场景。

线性读

etcd 默认读模式是线性读,因为它需要经过 Raft 协议模块反应的是集群共识,因此在延时和吞吐量上相比串行读略差一点,适用于对数据一致性要求高的场景。即一个值更新成功,随后任何通过线性读的 client 都能及时访问到。

线性读之 ReadIndex

串行读时之所以能读到旧数据,主要原因是当 client 发起一个写请求, Leader 收到写请求后会将此请求持久化到 WAL 日志,并广播给各个Follower节点,若一半以上节点持久化成功则该请求对应的日志条目被标识为已提交,Follower的etcdserver 模块再异步的从 Raft 模块获取已提交的日志条目,应用到状态机 (boltdb 等) 。

ReadIndex在 etcd 3.1 中引入的,当收到一个线性读请求时,Follower节点首先会向Raft 模块(即Leader)发送ReadIndex请求,此时Leader 会先向 Follower 节点发送心跳确认,一半以上节点确认 Leader 身份后才能将已提交日志索引 (committed index) 封装成 ReadState 结构体通过 channel 层层返回给线性读模块。线性读模块等待本节点状态机的已应用日志索引 (applied index) 大于等于 Leader 的已提交日志索引,就通知 KVServer 模块,可以与状态机中的 MVCC 模块进行交互读取数据了。

MVCC

MVCC多版本并发控制模块是为了解决提到 etcd v2 不支持保存 key 的历史版本、不支持多 key 事务等问题而产生的。它核心由内存树形索引模块 (treeIndex) 、嵌入式的 KV 持久化存储库 boltdb 、buffer组成。

其中boltdb是基于 B+ tree 实现的 key-value 键值库,支持事务,提供 Get/Put 等简易 API 给 etcd 操作。boltdb 的 key 是全局递增的版本号 (revision),value 是用户 key、value 等字段组合成的结构体,然后通过 treeIndex 模块来保存用户 key 和版本号的映射关系

treeIndex 模块是基于 Google 开源的内存版 btree 库实现的。

buffer则是在获取到版本号信息后,并不是所有请求都一定要从 boltdb 获取数据。etcd 出于数据一致性、性能等考虑,在访问 boltdb 前,首先会从一个内存读事务 buffer 中,二分查找你要访问 key 是否在 buffer 里面,若命中则直接返回。

img

img

etcd写请求如何执行

首先 client 端通过负载均衡算法选择一个 etcd 节点,发起 gRPC 调用。然后 etcd 节点收到请求后经过 gRPC 拦截器、Quota 模块后,进入 KVServer 模块,KVServer 模块向 Raft 模块提交一个提案,提案内容为“ put 一个 key 为 hello,value 为 world 的数据”。随后此提案通过 RaftHTTP 网络模块转发、经过集群多数节点持久化后,状态会变成已提交,etcdserver 从 Raft 模块获取已提交的日志条目,传递给 Apply 模块,Apply 模块通过 MVCC 模块执行提案内容,更新状态机。

Quota 模块

client 端发起 gRPC 调用到 etcd 节点,和读请求不一样的是,写请求需要经过Quota模块。当 etcd server 收到 put/txn 等写请求的时候,会首先检查下当前 etcd db 大小加上你请求的 key-value 大小之和是否超过了配额(quota-backend-bytes)。如果超过了配额,它会产生一个 NO SPACE的告警,并通过 Raft 日志同步给其它节点,告知 db 无空间了,并将告警持久化存储到 db 中。

因为etcd v3 是个 MVCC 数据库,保存了 key 的历史版本,当你未配置压缩策略(compact)的时候,随着数据不断写入,db 大小会不断增大,导致超限。压缩模块支持按多种方式回收旧版本,比如保留最近一段时间内的历史版本。不过它仅仅是将旧版本占用的空间打个空闲(Free)标记,后续新的数据写入的时候可复用这块空间而无需申请新的空间。

如果你需要回收空间减少 db 大小,得使用碎片整理(defrag), 它会遍历旧的 db 文件数据并写入到一个新的 db 文件。但是它对服务性能有较大影响,不建议你在生产集群频繁使用。

KVServer 模块

通过配额检查后,请求就从 API 层转发到了 KVServer 模块的 put 方法,我们知道 etcd 是基于 Raft 算法实现节点间数据复制的,因此它需要将 put 写请求内容打包成一个提案消息,提交给 Raft 模块。不过 KVServer 模块在提交提案前,还有如下的一系列检查和限速。

Preflight Check:为了保证集群稳定性,避免雪崩,任何提交到 Raft 模块的请求,都会做一些简单的限速判断。如果 Raft 模块已提交的日志索引(committed index)比已应用到状态机的日志索引(applied index)超过了 5000,那么它就返回一个"etcdserver: too many requests"错误给 client。

鉴权:尝试去获取请求中的鉴权信息,若使用了密码鉴权、请求中携带了 token,如果 token 无效,则返回"auth: invalid auth token"错误给 client。

包大小:会检查你写入的包大小是否超过默认的 1.5MB, 如果超过了会返回"etcdserver: request is too large"错误给给 client。

Propose

通过一系列检查之后,会生成一个唯一的 ID并将请求关联到一个对应的消息通知 channel,然后向 Raft 模块发起(Propose)一个提案(Proposal),即流程四。向 Raft 模块发起提案后,KVServer 模块会等待此 put 请求,等待写入结果通过消息通知 channel 返回或者超时。etcd 提交提案的默认超时时间是 7 秒(5 秒磁盘 IO 延时 +2*1 秒竞选超时时间),如果一个提案请求超时未返回结果,则可能会出现你熟悉的 etcdserver: request timed out 错误。

WAL 模块

Raft 模块收到提案后,如果当前节点是 Follower,它会转发给 Leader,只有 Leader 才能处理写请求。Leader 收到提案后,通过 Raft 模块输出 待转发给 Follower 节点的消息 和 待持久化的日志条目 给etcdserver,同时该etcdserver作为 Leader,它会将 put 提案消息广播给集群各个节点,此时leader及Follower节点需要需要把集群 Leader 任期号、投票信息、已提交索引、提案内容持久化到一个 WAL(Write Ahead Log)日志文件中,每个提案在被提交前都会各个节点被持久化到 WAL 日志文件中,以保证集群的一致性、可恢复性(crash后重启恢复),即流程五。

WAL 模块如何持久化 Raft 日志条目

首先将 Raft 日志条目内容(含任期号、已提交索引、提案内容)序列化后保存到 WAL 记录的 Data 字段, 然后计算 Data 的 CRC 值,设置 Type 为 Entry Type, 以上信息就组成了一个完整的 WAL 记录。最后计算 WAL 记录的长度,然后调用 fsync 持久化到磁盘,完成将Raft日志条目保存到持久化存储中。

一半以上节点持久化此Raft日志条目(WAL预写日志)后, Raft 模块就会通过 channel 告知 etcdserver 模块,put 提案已经被集群多数节点确认,提案状态为已提交,可以继续向Apply模块提交提案内容。

于是进入流程六,etcdserver 模块从 channel 取出提案内容,添加到先进先出(FIFO)调度队列,随后通过 Apply 模块按入队顺序,异步、依次执行提案内容。

Apply 模块

Apply 模块在执行提案内容前,会首先判断当前提案是否已经执行过了,如果执行过了则直接返回,若未执行同时无 db 配额满告警,则进入到 MVCC 模块,开始与持久化存储模块打交道,Apply模块执行 put 提案内容则对应流程七。

在考虑故障场景,若 put 请求提案在执行流程七的时候 etcd 突然 crash 了, 重启恢复的时候,etcd 是如何找回异常提案,再次执行呢。

核心就是我们上面介绍的 WAL 日志,因为提交给 Apply 模块执行的提案已获得多数节点确认即持久化到WAL日志文件中,etcd 重启时会从 WAL 中解析出 Raft 日志条目内容,追加到 Raft 日志的存储中,并重放已提交的日志提案到 Apply 模块执行

然而这又引发了另外一个问题,如何确保幂等性,防止提案重复执行导致数据混乱。

Raft 日志条目中的索引(index)字段是全局单调递增的,每个日志条目索引对应一个提案, 如果一个命令执行后,我们在 db 里面也记录下当前已经执行过的日志条目索引,则可以解决幂等性问题。

但是如果执行命令的请求更新成功了,但是更新 index 的请求却失败了,一样会导致异常,因此我们还需要将两个操作作为原子性事务提交,才能实现幂等。

MVCC

Apply 模块判断此提案未执行后,就会调用 MVCC 模块来执行提案内容。MVCC 主要由三部分组成,一个是内存索引模块 treeIndex,是一个内存版 BTree ,保存用户 key 和版本号 (revision)的映射关系;另一个是 boltdb 模块,是基于 B+tree 实现的 key-value 嵌入式 db,通过提供桶(bucket)机制实现类似 MySQL 表的逻辑隔离,boltdb 的 key 是全局递增的版本号,value 是用户 key、value 等字段组合成的结构体;另一个是bucket buffer 用来保存 合并后异步定时批量提交 导致的暂未提交的事务数据。

img

版本号(revision)在 etcd 里面发挥着重大作用,etcd 启动的时候默认版本号是 1,随着你对 key 的增、删、改操作而全局单调递增,boltdb则使用版本号作为key。

boltdb 的 value具体包括了用户key 值,value 值、key 创建时的版本号(create_revision)、最后一次修改时的版本号(mod_revision)、key 自身修改的次数(version),租约信息。

虽然boltdb put 调用成功,但etcd 并未提交事务,数据只更新在 boltdb 所管理的内存数据结构中。事务提交的过程,包含 B+tree 的平衡、分裂,将 boltdb 的脏数据(dirty page)、元数据信息刷新到磁盘,因此事务提交的开销是昂贵的。如果我们每次更新都提交事务,etcd 写性能就会较差。

etcd 采用是合并后异步定时批量提交,etcd 通过合并多个写事务请求,异步定时的(默认每隔 100ms)将批量事务一次性提交从而刷新到磁盘, 从而大大提高吞吐量,仅pending 事务过多才会触发同步提交。但这样也存在事务未提交,读请求可能无法从 boltdb 获取到最新数据的问题。

为了解决这个问题,etcd 引入了一个 bucket buffer 来保存暂未提交的事务数据。在更新 boltdb 的时候,etcd 也会同步数据到 bucket buffer。因此 etcd 处理读请求的时候会优先从 bucket buffer 里面读取,其次再从 boltdb 读,通过 bucket buffer 实现读写性能提升,同时保证数据一致性。

img

要点记录

1、线性读 ReadIndex需要等待本节点状态机的已应用日志索引 (applied index) 大于等于 Leader 的已提交日志索引,才能继续读取数据。

2、etcd 提交提案的默认超时时间是 7 秒,如果一个提案请求超时未返回结果,则会出现的 etcdserver: request timed out 错误。

3、当一半以上节点持久化了WAL日志后, Raft 模块才会通过 channel 告知 etcdserver 模块,put 提案已经被集群多数节点确认,提案状态为已提交,可以继续提交给Apply模块执行。

etcd 重启时会从 WAL预写式日志中解析出 Raft 日志条目内容,并重放已提交的日志提案到 Apply 模块执行。

etcd 在启动的时候会将etcd db文件内容拷贝到etcd进程内存中,启动拷贝是需要磁盘IO,节点内存足够的请求下,后续处理读请求过程中就不会产生磁盘 I/IO 了。

4、MVCC 主要由三部分组成,一个是内存索引模块 treeIndex,实现是BTree,用于保存用户 key 和版本号 (revision)的映射关系;另一个是 boltdb 模块,是基于 B+tree 实现的 key-value 嵌入式 db,通过提供桶(bucket)机制实现类似 MySQL 表的逻辑隔离,boltdb 的 key 是全局递增的版本号,value 是用户 key、value 等字段组合成的结构体;第三个是bucket buffer 用来保存 合并后异步定时批量提交 导致的暂未提交的事务数据。

5、etcd采用合并后异步定时批量提交,etcd 通过合并多个写事务请求,异步定时的(默认每隔 100ms)将事务批量一次性提交,进而刷新到磁盘, 从而大大提高吞吐量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/952128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis之管道解读

目录 基本介绍 使用例子 管道对比 管道与原生批量命令对比 管道与事务对比 使用pipeline注意事项 基准测试 基本介绍 Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务器。 这意味着请求通常按如下步骤处理: 客户端发送一个请求到服务器&am…

飞天使-python的模块与包与装饰器

文章目录 模块与包标准模块第三方模块自定义模块 高级语法切片迭代器/生成器高级模式(闭包)高级模式(装饰器) 参考视频 模块与包 标准模块 import os print(os.getcwd())import sys print(sys.argv) print(sys.platform) print(…

Three.js相机参数及Z-Fighting问题的解决方案

本主题讨论透视相机以及如何为远距离环境设置合适的视锥体。 推荐:用 NSDT编辑器 快速搭建可编程3D场景 透视相机是一种投影模式,旨在模仿人类在现实世界中看待事物的方式。 这是渲染 3D 场景最常用的投影模式。 - three.js 如果你看一下 Three.js 文档…

十四、组合模式

一、什么是组合模式 组合(Composite Pattern)模式的定义:有时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单…

极限五分钟,在宝塔中用 Docker 部署升讯威在线客服系统

最近客服系统成功经受住了客户现场组织的压力测试,获得了客户的认可。 客户组织多名客服上线后,所有员工同一时间打开访客页面疯狂不停的给在线客服发消息,系统稳定无异常无掉线,客服回复消息正常。消息实时到达无任何延迟。 本文…

IDEA maven上传速度很慢、解决办法

maven上传的速度很慢,排除网络原因,需要检查配置 一、项目配置 以下针对于maven仓库不在C盘的情况: File | Settings | Build, Execution, Deployment | Build Tools | Maven 以IDEA为例,打开 File(文件)…

【Vue3+Ts】项目启动准备和配置项目代码规范和css样式的重置

项目启动准备 创建项目( 使用Vite 构建工具创建项目模板)目录介绍插件安装创建别名编译说明项目配置配置icon和标题配置项目别名配置ts.config.json检测vscode的插件是否配置 配置项目代码规范集成editorconfig配置prettier工具库ESLint检测配置 CSS样式…

软件测试/测试开发丨Selenium 高级定位 Xpath

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接:https://ceshiren.com/t/topic/27036 一、xpath 基本概念 XPATH是一门在XML文档中查找信息的语言 XPATH使用路径表达式在XML文档中进行导航 XPATH的应用非常广泛,可以用于UI自…

使用Visual Studio 2022实现透明按钮和标签、POPUP样式窗体的一种工业系统的UI例程

例程实现的功能说明 1、主窗体采用POPUP样式,无标题栏、无菜单栏,适合工业类软件 2、按钮、标签使用自绘,实现透明样式,可以实现灵活的样式设计,更具设计感 按钮重绘函数:OnDrawItem()按钮样式设定&#…

微软 Turing Bletchley v3视觉语言模型更新:必应搜索图片更精准

据微软新闻稿透露,在推出第三代Turing Bletchley视觉语言模型后,微软计划逐步将其整合到Bing等相关产品中,以提供更出色的图像搜索体验。这款模型最初于2021年11月面世,并在2022年秋季开始邀请用户测试。 凭借用户的反馈和建议&am…

Spring Boot 中 Nacos 配置中心使用实战

官方参考文档 https://nacos.io/zh-cn/docs/quick-start-spring-boot.html 本人实践 1、新建一个spring boot项目 我的spirngboot版本为2.5.6 2、添加一下依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-…

FL studio21.1中文最新版FL萝卜音乐制作软件

FL studio21.1版中文名字叫做水果音乐编曲软件&#xff0c;是一款非常专业的电脑音乐制作软件。全新的21版本在功能上进行了升级&#xff0c;帮助用户来进行编曲、剪辑、录音、混音等多种工作&#xff0c;还可以为用户提供很多音乐创作的灵感&#xff0c;让你制作出各种好听的音…

postgresql-通用表表达式

postgresql-通用表表达式 简介简单 CTE递归 CTE案例1案例2 DML 语句与 CTE 简介 通用表表达式&#xff08;Common Table Expression、CTE&#xff09;是一个临时的查询结果或者临时表&#xff0c;可以 在其他 SELECT、INSERT、UPDATE 以及 DELETE 语句中使用。通用表表达式只在…

关闭jenkins插件提醒信息

jenkins提醒信息和安全警告可以帮助我们了解插件或者jenkins的更新情况&#xff0c;但是有些插件是已经不维护了&#xff0c;提醒却一直存在&#xff0c;看着糟心&#xff0c;就像下面的提示 1、关闭插件提醒 找到如下位置&#xff1a;系统管理-系统配置-管理监控配置 打开管…

SQL注入 - POST注入方法

文章目录 注入流程正常尝试弱口令登录判断是否存在注入&#xff0c;是否会将用户输入拼接到sql语句&#xff0c;并当做代码执行尝试是否需要闭合语句尝试注释掉后面得语句判断字段数判断显错位判断当前数据库名判断当前数据库下的表名判断当前数据库下users表的列名获取usernam…

DHCP中继实验

文章目录 一、实验背景与目的二、实验拓扑三、实验需求四、实验解法1. 配置IP地址2.配置R1为DHCP服务器&#xff0c;能够跨网段为192.168.2.0/24网段自动分配IP地址3. 在PC3上Ping 192.168.1.1&#xff0c;确认可以Ping通 摘要&#xff1a; 本实验旨在通过配置DHCP中继实现跨网…

stm32---用外部中断实现红外接收器

一、红外遥控的原理 红外遥控是一种无线、非接触控制技术&#xff0c;具有抗干扰能力强&#xff0c;信息传 输可靠&#xff0c;功耗低&#xff0c;成本低&#xff0c;易实现等显著优点&#xff0c;被诸多电子设备特别是 家用电器广泛采用&#xff0c;并越来越多的应用到计算机系…

电缆工厂 3D 可视化管控系统 | 智慧工厂

近年来&#xff0c;我国各类器材制造业已经开始向数字化生产转型&#xff0c;使得生产流程变得更加精准高效。通过应用智能设备、物联网和大数据分析等技术&#xff0c;企业可以更好地监控生产线上的运行和质量情况&#xff0c;及时发现和解决问题&#xff0c;从而提高生产效率…

2023-8-30 八数码(BFS)

题目链接&#xff1a;八数码 #include <iostream> #include <algorithm> #include <unordered_map> #include <queue>using namespace std;int bfs(string start) {string end "12345678x";queue<string> q;unordered_map<strin…

[ZenTao]源码阅读:自定义任务类型

1、module/custom/control.php 2、module/custom/model.php