etcd的Watch原理

news2024/11/13 10:12:39

在 Kubernetes 中,各种各样的控制器实现了 Deployment、StatefulSet、Job 等功能强大的 Workload。控制器的核心思想是监听、比较资源实际状态与期望状态是否一致,若不一致则进行协调工作,使其最终一致。

那么当你修改一个 Deployment 的镜像时,Deployment 控制器是如何高效的感知到期望状态发生了变化呢?

要回答这个问题,我们需要从etcd的Watch特性说起,他是Kubernetes控制器的工作基础。

那么实际上我们有4个核心问题:

  • client获取事件的机制,etcd是使用轮询模式还是推送模式呢?两者有什么优缺点?
  • 事件是如何存储的?会保留多久?watch命令中的版本号有什么作用?
  • 当client和server端出现短暂网络波动等异常因素后,导致事件堆积时,server端会丢弃事件吗?如果你监听的历史版本号server端不存在了,你的代码怎么处理?
  • 如果你创建了上万个 watcher 监听 key 变化,当 server 端收到一个写请求后,etcd 是如何根据变化的 key 快速找到监听它的 watcher 呢?

接下来我就和你分别详细聊聊 etcd Watch 特性是如何解决这四大问题的。搞懂这四个问题,你就明白 etcd 甚至各类分布式存储 Watch 特性的核心实现原理了。

轮询 VS 流式推送

首先第一个问题是client获取事件机制,etcd是使用轮询还是推送模式呢?答案是etcd两者都使用过。

在etcd v2 Watch机制实现中,使用的是HTTP/1.x协议,实现简单,每一个watcher对应一个TCP连接。client通过HTTP/1.1协议长连接定时轮询server获取最新的数据变化事件。

但是当你的watcher成千上万的时候,即使集群空负载,大量轮询也会产生一定的QPS,server端会消耗大量的socket,内存等资源,导致etcd的稳定性无法满足Kubernetes的要求。

etcd v3 的 Watch 机制的设计实现并非凭空出现,它正是吸取了 etcd v2 的经验、教训而重构诞生的。

在 etcd v3 中,为了解决 etcd v2 的以上缺陷,使用的是基于 HTTP/2 的 gRPC 协议,双向流的 Watch API 设计,实现了连接多路复用。

HTTP/2 协议为什么能实现多路复用呢?

在这里插入图片描述

在 HTTP/2 协议中,HTTP 消息被分解独立的帧(Frame),交错发送,帧是最小的数据单位。每个帧会标识属于哪个流(Stream),流由多个数据帧组成,每个流拥有一个唯一的 ID,一个数据流对应一个请求或响应包。

如上图所示,client 正在向 server 发送数据流 5 的帧,同时 server 也正在向 client 发送数据流 1 和数据流 3 的一系列帧。一个连接上有并行的三个数据流,HTTP/2 可基于帧的流 ID 将并行、交错发送的帧重新组装成完整的消息。

通过以上机制,HTTP/2 就解决了 HTTP/1 的请求阻塞、连接无法复用的问题,实现了多路复用、乱序发送。

etcd 基于以上介绍的 HTTP/2 协议的多路复用等机制,实现了一个 client/TCP 连接支持多 gRPC Stream, 一个 gRPC Stream 又支持多个 watcher,如下图所示。同时事件通知模式也从 client 轮询优化成 server 流式推送,极大降低了 server 端 socket、内存等资源。

在这里插入图片描述

HTTP2.0的详细知识请看《小林coding》

当然在 etcd v3 watch 性能优化的背后,也带来了 Watch API 复杂度上升, 不过你不用担心,etcd 的 clientv3 库已经帮助你搞定这些棘手的工作了。

在 clientv3 库中,Watch 特性被抽象成 Watch、Close、RequestProgress 三个简单API 提供给开发者使用,屏蔽了 client 与 gRPC WatchServer 交互的复杂细节,实现了一个 client 支持多个 gRPC Stream,一个 gRPC Stream 支持多个 watcher,显著降低了你的开发复杂度。

同时当 watch 连接的节点故障,clientv3 库支持自动重连到健康节点,并使用之前已接收的最大版本号创建新的 watcher,避免旧事件回放等。

滑动窗口 VS MVCC

介绍完 etcd v2 的轮询机制和 etcd v3 的流式推送机制后,再看第二个问题,事件是如何存储的? 会保留多久呢?watch 命令中的版本号具有什么作用?

第二个问题的本质是历史版本存储,etcd 经历了从滑动窗口到 MVCC 机制的演变,滑动窗口是仅保存有限的最近历史版本到内存中,而 MVCC 机制则将历史版本保存在磁盘中,避免了历史版本的丢失,极大的提升了 Watch 机制的可靠性。

etcd v2 滑动窗口是如何实现的?它有什么缺点呢?

它使用的是如下一个简单的环形数组来存储历史事件版本,当 key 被修改后,相关事件就会被添加到数组中来。若超过 eventQueue 的容量,则淘汰最旧的事件。在 etcd v2 中,eventQueue 的容量是固定的 1000,因此它最多只会保存 1000 条事件记录,不会占用大量 etcd 内存导致 etcd OOM。

type EventHistory struct {
    Queue eventQueue
    StartIndex uint64
    LastIndex uint64
    rwl sync.RWMutex
}

但是它的缺陷显而易见的,固定的事件窗口只能保存有限的历史事件版本,是不可靠的。当写请求较多的时候、client 与 server 网络出现波动等异常时,很容易导致事件丢失,client 不得不触发大量的 expensive 查询操作,以获取最新的数据及版本号,才能持续监听数据。

特别是对于重度依赖 Watch 机制的 Kubernetes 来说,显然是无法接受的。因为这会导致控制器等组件频繁的发起 expensive List Pod 等资源操作,导致 APIServer/etcd 出现高负载、OOM 等,对稳定性造成极大的伤害。

etcd v3 的 MVCC 机制,正如上一节课所介绍的,就是为解决 etcd v2 Watch 机制不可靠而诞生。相比 etcd v2 直接保存事件到内存的环形数组中,etcd v3 则是将一个 key 的历史修改版本保存在 boltdb 里面。boltdb 是一个基于磁盘文件的持久化存储,因此它重启后历史事件不像 etcd v2 一样会丢失,同时你可通过配置压缩策略,来控制保存的历史版本数,在压缩篇我会和你详细讨论它。

最后 watch 命令中的版本号具有什么作用呢?

版本号是 etcd 逻辑时钟,当 client 因网络等异常出现连接闪断后,通过版本号,它就可从 server 端的 boltdb 中获取错过的历史事件,而无需全量同步,它是 etcd Watch 机制数据增量同步的核心。

可靠的事件推送机制

再看第三个问题,当 client 和 server 端出现短暂网络波动等异常因素后,导致事件堆积时,server 端会丢弃事件吗?若你监听的历史版本号 server 端不存在了,你的代码该如何处理?

第三个问题的本质是可靠事件推送机制,要搞懂它,我们就得弄懂 etcd Watch 特性的整体架构、核心流程,下图是 Watch 特性整体架构图。

在这里插入图片描述

当你通过 etcdctl 或 API 发起一个 watch key 请求的时候,etcd 的 gRPCWatchServer收到 watch 请求后,会创建一个 serverWatchStream, 它负责接收 client 的 gRPC Stream 的 create/cancel watcher 请求 (recvLoop goroutine),并将从 MVCC 模块接收的 Watch 事件转发给 client(sendLoop goroutine)。

当 serverWatchStream 收到 create watcher 请求后,serverWatchStream 会调用MVCC 模块的 WatchStream 子模块分配一个 watcher id,并将 watcher 注册到 MVCC的 WatchableKV 模块。

在 etcd 启动的时候,WatchableKV 模块会运行 syncWatchersLoop 和syncVictimsLoop goroutine,分别负责不同场景下的事件推送,它们也是 Watch 特性可靠性的核心之一。

从架构图中你可以看到 Watch 特性的核心实现是 WatchableKV 模块,下面我就为你抽丝剥茧,看看"etcdctl watch hello -w=json --rev=1"命令在 WatchableKV 模块是如何处理的?面对各类异常,它如何实现可靠事件推送?

etcd核心解决方案是复杂度管理,问题拆分

它通过将 watcher 划分为synced/unsynced/victim 三类,将问题进行了分解,并通过多个后台异步循环goroutine 负责不同场景下的事件推送,提供了各类异常等场景下的 Watch 事件重试机制,尽力确保变更事件不丢失、按逻辑时钟版本号顺序推送给 client。

高效事件匹配机制

介绍完可靠的事件推送机制后,最后我们再看第四个问题,如果你创建了上万个 watcher监听 key 变化,当 server 端收到一个写请求后,etcd 是如何根据变化的 key 快速找到监听它的 watcher 呢?一个个遍历 watcher 吗?

显然一个个遍历 watcher 是最简单的方法,但是它的时间复杂度是 O(N),在 watcher 数较多的场景下,会导致性能出现瓶颈。更何况 etcd 是在执行一个写事务结束时,同步触发事件通知流程的,若匹配 watcher 开销较大,将严重影响 etcd 性能。

那使用什么数据结构来快速查找哪些 watcher 监听了一个事件中的 key 呢?

也许你会说使用 map 记录下哪些 watcher 监听了什么 key 不就可以了吗? etcd 的确使用 map 记录了监听单个 key 的 watcher,但是你要注意的是 Watch 特性不仅仅可以监听单 key,它还可以指定监听 key 范围、key 前缀,因此 etcd 还使用了如下的区间树

在这里插入图片描述

当收到创建 watcher 请求的时候,它会把 watcher 监听的 key 范围插入到上面的区间树中,区间的值保存了监听同样 key 范围的 watcher 集合 /watcherSet。

当产生一个事件时,etcd 首先需要从 map 查找是否有 watcher 监听了单 key,其次它还需要从区间树找出与此 key 相交的所有区间,然后从区间的值获取监听的 watcher 集合。

区间树支持快速查找一个 key 是否在某个区间内,时间复杂度 O(LogN),因此 etcd 基于map 和区间树实现了 watcher 与事件快速匹配,具备良好的扩展性。

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

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

相关文章

数据结构篇三:双向循环链表

文章目录 前言双向链表的结构功能的解析及实现1. 双向链表的创建2. 创建头节点(初始化)3. 创建新结点4. 尾插5. 尾删6. 头插7. 头删8. 查找9. 在pos位置前插入10. 删除pos位置的结点11. 销毁 代码实现1.ListNode.h2. ListNode.c3. test.c 总结 前言 前面…

03-WAF绕过-漏洞利用之注入上传跨站等绕过

WAF绕过-漏洞利用之注入上传跨站等绕过 思维导图 一、sql注入绕过 使用sqlmap注入测试绕过 1.绕过cc流量 通过sqlmap对网站进行测试的时候,如果对方有cc流量防护,需要给sqlmap设置一个代理进行注入。 防cc拦截:修改user-agent头代理&…

ADB调试删除手机内置应用

前言 最近手机升级到了鸿蒙3系统,但是内置了两个输入法,我想删掉小艺输入法,于是就有了这篇记录。   本文在B站上ADB调试卸载应用的教程的基础上,去掉了内网穿透相关操作步骤。 前期准备 手机(荣耀10青春版&#x…

3.4 只读存储器

学习目标: 学习只读存储器(ROM)的目标可以包括以下内容: 了解ROM的基本概念、分类以及适用场景。掌握ROM的电路原理、逻辑结构和读取方式。熟悉ROM的编程方式和编程工具。理解ROM与EPROM、EEPROM和闪存的区别和联系。了解ROM在计…

IPsec中IKE与ISAKMP过程分析(快速模式-消息1)

IPsec中IKE与ISAKMP过程分析(主模式-消息1)_搞搞搞高傲的博客-CSDN博客 IPsec中IKE与ISAKMP过程分析(主模式-消息2)_搞搞搞高傲的博客-CSDN博客 IPsec中IKE与ISAKMP过程分析(主模式-消息3)_搞搞搞高傲的博客…

[架构之路-181]-《软考-系统分析师》-19- 系统可靠性分析与设计 - 概览

前言: 可靠性工程是研究产品生命周期中故障的发生、发展规律,达到预防故障,消灭故 障,提高产品可用性的工程技术。 信息系统的可靠性是指系统在满足一定条件的应用环境中能够正常工作的能力,可以按一般工程系统的可靠性…

图像生成论文阅读:GLIDE算法笔记

标题:GLIDE: Towards Photorealistic Image Generation and Editing with Text-Guided Diffusion Models 会议:ICML2022 论文地址:https://proceedings.mlr.press/v162/nichol22a.html 官方代码:https://github.com/openai/glide-…

【算法】回文数

目录 一.回文数 二.求回文数(10000以内) 代码: 翻译: 调试: 三.判断回文数 代码: 调试: 一.回文数 "回文数"是一种数字。 如:12321, 这个数字正读是12321,倒读也是…

C++的类

文章目录 class定义类声明和定义不分离成员函数声明与定义的分离 类的访问限定符类的实例化类对象的大小this指针 引入:什么是类呢? 在C语言阶段,结构体成员只能是它的属性,这个结构体就相当于张三,小时候它只被赋予了名字,性别,家庭住址等属性,但是他没…

docker Mysql部署主从集群

目录 1 docker安装 2 docker mysql 安装配置 远程连接 2.0 配置 2.0.1 文件夹 配置 2.0.2 主库文件配置 my.cnf -> 主库 id 和 开启二进制日志 2.0.3 从库文件配置 -> 从库 id 2.1 mysql 主 -> 第一个端口号和从库不一样 2.1.1 docker run 主库 2.1.2 查看主…

Postman创建项目 对接口发起请求处理

查看本文之前 您需要理解了解 Postman 的几个简单工作区 如果还没有掌握 可以先查看我的文章 简单认识 Postman界面操作 那么 掌握之后 我们就可以正式来开启我们的接口测试 我们先选择 Collections 我们点上面这个加号 多拉一个项目出来 然后 我们选我们刚加号点出来的项目…

用LangChain构建大语言模型应用

用LangChain构建大语言模型应用 自 ChatGPT 发布以来,大型语言模型 (LLM) 广受欢迎。尽管您可能没有足够的资金和计算资源从头开始训练自己的大语言模型,但您仍然可以使用预训练的大语言模型来构建一些很酷的东西,例如: 可以根据…

01-权限提升-网站权限后台漏洞第三方获取

权限提升-网站权限后台漏洞第三方获取 本节课内容主要是权限提升的思路,不涉及技术 当前知识点在渗透流程中的点 前期-中期-后期对应知识关系 当前知识点在权限提升的重点 知识点顺序,理解思路,分类介绍等 当前知识点权限提升权限介绍 …

Java8

Java8 (一)、双列集合(二)、Map集合常用api(三)、Map集合的遍历方式(四)、HashMap(五)、LinkedHashMap(六)、TreeMap(七&a…

Steve:AI创建视频和动画的在线工具

【产品介绍】 steve.ai是一款利用人工智能技术创建视频和动画的在线工具,可以让任何人在几分钟内把文字转换成吸引人的视频。核心功能是根据用户输入的文本,自动选择合适的素材、音乐、字幕和动效,生成高质量的视频。还提供了多种模板、风格和…

Photoshop如何使用滤镜之实例演示?

文章目录 0.引言1.将普通照片制作成油画效果2.使用液化滤镜修出完美身材3.用镜头光晕滤镜制作唯美的逆光人像4.用Camera Raw滤镜对偏色风景照进行调色 0.引言 因科研等多场景需要进行绘图处理,笔者对PS进行了学习,本文通过《Photoshop2021入门教程》及其…

Servlet 笔记

1. HTTP 协议 1.1 HTTP协议简介 超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。 HTTP的发展是由蒂姆伯纳斯-…

etcd原理剖析一

为什么Kubernetes使用etcd? 首先我们来看服务高可用以及数据一致性。单副本存在单点故障,而多副本又引入数据一致性问题。 为了解决数据一致性问题,需要引入一个共识算法。例如Raft等。etcd选择了Raft,它将复杂的一致性问题分解…

Maven 笔记

1. Maven 的简介 1.1 简介 Maven 这个词可以翻译为"专家","内行"。作为Apache 组织中的一个开源项目,主要服务于基于java平台的项目构建,依赖管理和项目信息管理。 无论是小型的开源类库项目,还是大型的企业级应用&am…

Spring 5 笔记 - 入门与IOC

1. Spring 入门简介 Spring:轻量级、开源的JavaEE框架, 解决企业应用的复杂性。包括IOC和AOP两个核心部分。 IOC: 控制反转,把创建对象和对象之间的调用的过程都交给Spring 进行管理,使耦合度降低。 AOP&#xff1a…