- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
@ 。
etcd 官网地址 https://etcd.io/ 最新版本3.5.7 。
etcd 官网文档地址 https://etcd.io/docs/v3.5/ 。
etcd 源码地址 https://github.com/etcd-io/etcd 。
etcd是一个强一致、可靠的分布式键值存储,使用Go语言开发(docker和k8s也是),其提供可靠的分布式键值(key-value)存储、配置共享和服务发现等功能,即使在集群脑裂网络分区情况下也可以优雅地处理leader选举;官方上有明确说明etcd是一个CNCF项目。可以说,etcd 已经成为了云原生和分布式系统的存储基石.
分布式系统中的数据分为控制数据和应用数据。etcd的使用场景默认处理的数据都是控制数据,对于应用数据,只推荐数据量很小,但是更新访问频繁的情况。应用场景有如下几类 。
如果需要一个分布式存储仓库来存储配置信息,并且希望这个仓库读写速度快、支持高可用、部署简单、支持http接口,那么就可以使用云原生项目etcd.
etcd实现的绝大多数功能Zookeeper都能实现,那为何还要用etcd?相较之下,Zookeeper有如下缺点:
而etcd作为一个后起之秀,对比Zookeeper其优点如下 。
etcd作为一个年轻的项目,正在高速迭代和开发中,这既优点也是缺点。优点在于它的未来具有无限的可能性,缺点是版本的迭代导致其使用的可靠性无法保证,无法得到大项目长时间使用的检验。但由于CoreOS、Kubernetes和Cloudfoundry等知名项目均在生产环境中使用了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 协议。etcdv3版本中client 和 server 之间的通信,使用的是基于 HTTP/2 的 gRPC 协议。相比 etcd v2 的 HTTP/1.x,HTTP/2 是基于二进制而不是文本、支持多路复用而不再有序且阻塞、支持数据压缩以减少包大小、支持 server push 等特性。因此,基于 HTTP/2 的 gRPC 协议具有低延迟、高性能的特点,有效解决etcd v2 中 HTTP/1.x 性能问题.
Raft 算法层:Raft 算法层实现了 Leader 选举、日志复制、ReadIndex 等核心算法特性,用于保障 etcd 多个节点间的数据一致性、提升服务可用性等,是 etcd 的基石和亮点.
功能逻辑层:etcd 核心特性实现层,如典型的 KVServer 模块、MVCC 模块、Auth 鉴权模块、Lease 租约模块、Compactor 压缩模块等,其中 MVCC 模块主要由 treeIndex(内存树形索引) 模块和 boltdb(嵌入式的 KV 持久化存储库) 模块组成。treeIndex 模块使用B-tree 数据结构来保存用户 key 和版本号的映射关系,使用B-tree是因为etcd支持范围查询,使用hash表不适合,从性能上看,B-tree相对于二叉树层级较矮,效率更高;boltdb是个基于 B+ tree 实现的 key-value 键值库,支持事务,提供 Get/Put 等简易 API 给 etcd 操作.
存储层:存储层包含预写日志 (WAL) 模块、快照 (Snapshot) 模块、boltdb 模块。其中 WAL 可保障 etcd crash 后数据不丢失,boltdb 则保存了集群元数据和用户写入的数据.
etcd 是典型的读多写少存储,在我们实际业务场景中,读一般占据 2/3 以上的请求.
读请求:客户端通过负载选择一个etcd节点发出读请求,API接口层提供Range RPC方法,etcd服务端拦截gRPC 读请求后调用的处理请求.
写请求:客户端通过负载均衡选择一个etcd节点发起请求etcd服务端拦截gRPC写请求,涉及校验和监控后KVServer向raft模块发起提案,内容写入数据命令,经过网络转发,当集群中多数节点达成一致持久化数据后,状态变更MVCC模块执行提案内容.
etcd客户端工具通过etcdctl执行一个读命令,解析完请求中的参数创建clientv3 库对象,然后通过EndPoint列表使用Round-Robin负载均衡算法选择一个etcd server节点,调用 KVServer API模块基于 HTTP/2 的 gRPC 协议的把请求发送给 etcd server,拦截器拦截,主要做一些校验和监控,然后调用KVserver模块的Range接口获取数据。读操作的核心步骤:
线性读是相对串行读来讲的概念,集群模式下会有多个etcd节点,不同节点间可能存在一致性的问题。串行读直接返回状态数据,不需要与集群中其他节点交互。这种方式速度快,开销小,但是会存在数据不一致的情况.
线性读则需要集群成员之间达成共识,存在开销,响应速度相对慢,但是能保证数据的一致性,etcd默认的读模式线性读.
etcd中查询请求,查询单个键或者一组键及查询数量,到底层实际会调用Range keys方法.
流程如下:
ReadTx和BatchTx是两个几口,用于读写请求创建Backend结构体,默认也会创建readTx和batchTx。readTx实现了ReadTx,负责处理只读请求batchTx,实现了BatchTx接口,负责处理读写请求.
对于上层的键值存储,它会利用返回的Revision从正真的存储数据中的BoltDB中,查询当前key对应的Revsion数据。BoltDB内部用类似buctket的方式存储对应MySQL中的表结构,用户key数据存放bucket的名字是key etcd mvcc元数据存放bucket的meta.
核心模块的功能:
写操作涉及核心模块功能如下:
Quoto模块 。
KVServer模块 。
WAL模块 。
Apply模块 。
MVCC模块 。
日志由一个个递增的有序序号索引标识。Leader维护了所有Follow节点的日志复制进度,在新增一个日志后,会将其广播给所有Follow节点。Follow节点处理完成后,会告知Leader当前已复制的最大日志索引。Leader收到后,会计算被一半以上节点复制过的最大索引位置,标记为已提交位置,在心跳中告诉Follow节点。只有被提交位置以前的日志才会应用到存储状态机.
在本地安装、运行和测试etcd的单成员集群,部署详细可以查看下上一篇《云原生API网关全生命周期管理Apache APISIX探究实操》中有关于etcd单节点部署,单节点部署完毕后验证读写和查看版本信息如下:
静态地启动etcd集群要求集群中的每个成员都认识集群中的其他成员;但通常集群成员的ip可能事先未知,可以通过发现服务引导etcd集群。在生产环境中,为了整个集群的高可用,etcd 正常都会集群部署,避免单点故障。引导 etcd 集群的启动有以下三种机制:
在部署之前已经知道了集群成员、它们的地址和集群的大小,name可以通过设置initial-cluster标志来使用脱机引导配置。分别在各个节点上执行下面语句 。
etcd --name infra1 --initial-advertise-peer-urls http://192.168.3.111:2380 \
--listen-peer-urls http://192.168.3.111:2380 \
--listen-client-urls http://192.168.3.111:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.3.111:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra1=http://192.168.3.111:2380,infra2=http://192.168.3.112:2380,infra3=http://192.168.3.113:2380 \
--initial-cluster-state new
etcd --name infra2 --initial-advertise-peer-urls http://192.168.3.112:2380 \
--listen-peer-urls http://192.168.3.112:2380 \
--listen-client-urls http://192.168.3.112:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.3.112:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra1=http://192.168.3.111:2380,infra2=http://192.168.3.112:2380,infra3=http://192.168.3.113:2380 \
--initial-cluster-state new
etcd --name infra3 --initial-advertise-peer-urls http://192.168.3.113:2380 \
--listen-peer-urls http://192.168.3.113:2380 \
--listen-client-urls http://192.168.3.113:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.3.113:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra1=http://192.168.3.111:2380,infra2=http://192.168.3.112:2380,infra3=http://192.168.3.113:2380 \
--initial-cluster-state new
也可以通过nohup &后台启动etcd,获取集群的member信息 。
etcdctl --endpoints=192.168.5.52:2379 member list
# 创建日志目录
mkdir /var/log/etcd
# 创建数据目录
mkdir /data/etcd
mkdir /home/commons/data/etcd
发现URL标识唯一的etcd集群。每个etcd实例共享一个新的发现URL来引导新集群,而不是重用现有的发现URL。如果没有可用的现有集群,则使用discovery.etc .io托管的公共发现服务。要使用“new”端点创建一个私有发现URL,使用命令
# 通过curl生成
curl https://discovery.etcd.io/new?size=3
https://discovery.etcd.io/d45c453e99404bcb4b0b30b0ff924200
# 通过上面返回组装
ETCD_DISCOVERY=https://discovery.etcd.io/d45c453e99404bcb4b0b30b0ff924200
--discovery https://discovery.etcd.io/d45c453e99404bcb4b0b30b0ff924200
分别在各个节点上执行下面语句 。
etcd --name myectd1 --data-dir /home/commons/data --initial-advertise-peer-urls http://192.168.5.111:2380 \
--listen-peer-urls http://192.168.5.111:2380 \
--listen-client-urls http://192.168.5.111:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.5.111:2379 \
--discovery https://discovery.etcd.io/d45c453e99404bcb4b0b30b0ff924200
etcd --name myectd2 --data-dir /home/commons/data --initial-advertise-peer-urls http://192.168.5.112:2380 \
--listen-peer-urls http://192.168.5.112:2380 \
--listen-client-urls http://192.168.5.112:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.5.112:2379 \
--discovery https://discovery.etcd.io/d45c453e99404bcb4b0b30b0ff924200
etcd --name myectd3 --data-dir /home/commons/data --initial-advertise-peer-urls http://192.168.5.113:2380 \
--listen-peer-urls http://192.168.5.113:2380 \
--listen-client-urls http://192.168.5.113:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.5.113:2379 \
--discovery https://discovery.etcd.io/d45c453e99404bcb4b0b30b0ff924200
#写入KV
etcdctl put /key1 value1
etcdctl put /key2 value2
etcdctl put /key3 value3
# 范围,左闭右开
etcdctl get /key1 /key3
# 以十六进制格式读取key foo值的命令:
etcdctl get /key1 --hex
# 仅打印value
etcdctl get /key1 --print-value-only
# 前缀匹配和返回条数
etcdctl get --prefix /key --limit 2
# 按照key的字典顺序读取,大于或等于
etcdctl get --from key /key1
# 监听key,可以获取key变更信息
etcdctl watch /key1
# 重新修改
etcdctl put /key1 value111
# 读取版本
etcdctl get /key1 --rev=5
# 删除key
etcdctl del /key3
# 租约,例如授予60秒生存时间的租约
etcdctl lease grant 60
lease 5ef786eee44b831d granted with TTL(60s)
# 写入带租约
etcdctl put --lease=5ef786eee44b831d /key4 value4
# 撤销租约
etcdctl lease revoke 32695410dcc0ca06
# 授权创建角色
etcdctl role add testrole
etcdctl role list
etcdctl role grant-permission testrole read /permission
etcdctl role revoke-permission testrole /permission
etcdctl role del testrole
# 授权创建用户
etcdctl user add testuser
etcdctl user list
etcdctl user passwd
etcdctl user get testuser
etcdctl user del testuser
etcdctl user grant-role testuser testrole
# 创建测试账号2
etcdctl role add testrole2
etcdctl role grant-permission testrole2 readwrite /permission
etcdctl user add testuser2
etcdctl user grant-role testuser2 testrole2
#1. 添加root角色
etcdctl role add root
#2. 添加root用户
etcdctl user add root
#3. 给root用户授予root角色
etcdctl user grant-role root root
#4.激活auth
etcdctl auth enable
etcdctl put /permission all2 --user=testuser2
etcdctl get /permission --user=testuser2
etcdctl get /permission --user=testuser
etcdctl put /permission allhello --user=testuser
# 直接带上密码
etcdctl --user='testuser2' --password='123456' put /permission all2
# 集群鉴权
etcdctl --endpoints http://192.168.3.111:2379,http://192.168.3.111:2379,http://192.168.3.111:2379 --user=root --password=123456 auth enable
etcdctl --endpoints http://192.168.3.111:2379,http://192.168.3.111:2379,http://192.168.3.111:2379 --user=root:123456 auth enable
最后此篇关于云原生K8S精选的分布式可靠的键值存储etcd原理和实践的文章就讲到这里了,如果你想了解更多关于云原生K8S精选的分布式可靠的键值存储etcd原理和实践的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在 php 方面遇到了一个小问题,我发现很难用语言来解释。我有一个包含键值的关联数组。我想制作一个函数(或者如果已经有一个函数),它将一个数组作为输入并删除重复项,但两种方式都是如此。 例如: 在我
我有一个在系统托盘中运行的应用程序,是否可以允许用户通过 C# 中的 Windows 键 + 键 恢复该应用程序? 谢谢 最佳答案 是的,使用 Windows API。我认为 Windows 键与 C
我正在使用 Waterline通过 Sails 查询 MySQL 数据库。我找到了 2 种方法。 不知道哪个更好? 顺便问一下,如何处理这两种情况的错误? 1. Model.findOne().whe
我正在尝试测试是否按下了 Alt 键。 我的支票类似于: private void ProcessCmdKey(Keys keyData) { if (keyData == Keys.Alt) {
我正在使用 Selenium WebDriver 和 Ruby 进行自动化测试。我需要点击一个按钮。我无法通过 id 或 css 或 xpath 获取按钮元素,因为按钮是透明的。我想使用 Tab 和
我是 IntelliJ 的新手,我看到一个启动提示说,“任何工具窗口中的 ⎋ 键都会将焦点移动到编辑器。”但是,我不知道⎋键是什么。我一直在编程很长时间。我的键盘上可能有一个我多年来一直错过的键吗?
我使用 OMDB API 创建了一个电影搜索页面。我遇到的问题是,如果我搜索一部包含多个单词的电影,此 API 会出错,因为 API 的 URL 必须在 URL 中的每个单词之间有 + 键。所以我想知
我已经用 Elasticsearch 玩了大约一天了,所以我非常陌生。我正在尝试 POST/import 一个简单的文件: { "compression" : "none", "com
enter image description here 在此示例中,要记录带有“title”和“director”键的属性值,使用 obj[key]。因为我们已经处于对象的执行上下文中:在本例中是电
我是新开类。 我使用新的电子邮件 ID 和密码在 openshift 上创建了一个项目。让我们称之为 firstApp 。我做了 rhc 设置和我的 ssh key 与我的项目相关联。 我的 frie
当我使用 Jackson 反序列化 json 字符串时,我通常不想创建所有 bean 类的属性,而且我只需要一些 json 字符串的字段,其他字段我不需要。所以我经常只在我需要的 java 类 bea
我想编写一个带有 keys/keys* 的规范,但能够内联值规范,但不支持 by design ,我明白了其背后的原因。然而,有时,本地图存在特定上下文时,您确实希望(或者只是通过遗留或第三方)键和值
my %fruit_colors = ("apple", "red", "banana", "yellow"); my @fruits = keys %fruit_colors; my @colors
我正在使用 vb.net 2008 和 DataGridView。我正在寻找允许我将 enter 键移动到右侧的下一列而不是在保持在同一列时向下移动一行的代码。 最佳答案 如果您正在确认编辑,只需移动
我刚刚开始学习编码,我遇到了这个我无法理解的问题。 “我们将添加的第二个函数称为搜索,它将以名字作为参数。它将尝试将收到的名字与我们 friend 联系人列表中的任何名字相匹配。如果它找到匹配项,就会
我已经在 Python 中运行了下面的代码,以从文本文件中生成单词列表及其计数。我该如何从“Frequency_list”变量中过滤掉计数为 1 的单词? 另外,如何将底部的打印语句循环导出到CSV
我正在尝试 XSLT 中的查找表示例,但无法使其正常工作
是否可以在 Javascript/Typescript 中编写一个将参数名称/键作为字符串返回的函数? function foo(arg) {...} let user = new User(); f
我正在尝试创建一个带有键/值的对象,但是当我看到该对象时,键没有正确填充.. 我希望键是 - 0,1,2,3 但它显示“索引”作为键。 > categories = ["09/07/2016 00:0
将 Android Studio 从 1.5 升级到 2.0 后,模拟器(现在版本为 25.1.1,我在其上配置了模拟硬件键盘)不再将 [Esc] 键识别为等同于 [Back] 按钮。 如何恢复这个有
我是一名优秀的程序员,十分优秀!