https://xie.infoq.cn/article/3329de088beb60f5803855895
  • 一、白话 etcd 与 zookeeper

  • 二、etcd 的 4 个核心机制

  • 三、Leader 选举与客户端交互

  • 四、etcd 的应用场景

  • 4.1. kubernetes 大脑

  • 4.2. 服务注册与发现

  • 4.3. 健康检查与状态变更通知

  • 4.4.分布式锁

  • 4.5.实现消息队列(纯扯淡)

  • 五、etcd 安装

  • 六、jetcd 的编码实现配置管理

大家好啊,我是字母哥,今天写一篇关于 etcd 的文章,其实网上也有很多关于 etcd 的介绍,「我就简明扼要,总结提炼,期望大家通过这一篇文章掌握 etcd 的核心知识以及编码技能」!

  • 本文首先用大白话给大家介绍一下 etcd 是什么?这部分内容网上已经有很多了。

  • etcd 有哪些应用场景?这些应用场景的核心原理是什么?

  • 最后不能光动嘴不动手。先搭建一个 etcd 单机版,再使用 java 的客户端操作 etcd 数据。

本文旨在帮助大家理解 etcd,从宏观角度俯瞰 etcd 全局,掌握 etcd 的基本操作技能。后续我还会写一个系列的文章,将每一种应用场景代码化,期待大家关注我和我的公众号:字母哥杂谈。后续计划章节内容如下:

  • 《搭建高可用 etcd 集群》

  • 《基于 etcd 实现分布式锁(java 代码实现)》

  • 《基于 etcd 实现配置变更通知(java 代码实现)》

  • 《基于 etcd 实现服务注册与发现(java 代码实现)》

  • 《基于 etcd 实现分布式系统节点 leader 选举(java 代码实现)》

一、白话 etcd 与 zookeeper

用过 linux 的朋友请举手,好的,我看见了! 在 linux 中所有自动安装的系统软件配置文件都存储在一个名为/etc的目录中。“d”表示「distributed」分布式,etcd 为分布式模型,所以 etcd 的核心应用场景是:「分布式系统的配置信息存储」。

网上很多文章上来第一句话照搬英文官网:「etcd 是一个高度一致的分布式键值存储系统」。很多朋友看完就问了,这玩意和 redis 有啥区别? 笔者要说,真的不要这么比,etcd 从名字上就已经告诉你了,它是存储配置信息(元数据)的。和 redis 在架构应用上就不在一个层面,它对标的产品应该是 zookeeper。虽然 zookeeper 在很多 java 的分布式系统的应用中比较广泛,但是 etcd 作为后起之秀,乘 kubernetes 的东风,大有超越 zookeeper 的趋势。

  • zookeeper 是使用 java 写的, etcd 是使用 go 语言编写的。zookeeper 使用了 TCP 协议,其交互报文规则是完全自定义的,如果不使用 zookeeper 提供的 SDK 就无法操作数据。而 etcd 使用的是 google 的 gRPC 协议,普适性更好一些。

  • zookeeper 对于一次请求,开启一个 socket 进行监听。而 etcd 的监听管道 channel 可以反复被利用,从 IO 性能到系统资源的利用的角度,etcd 无疑是更优秀的。

  • zookeeper 使用 zab 协议保证集群节点配置信息的一致性,etcd 使用 raft 协议。期望详细了解 raft 协议的,点击《raft协议中文介绍》

「大部分功能和 zookeeper 都是一样的,目前看 java 程序员用 zookeeper 的更多,其他程序员用 etcd 更多。都是基于习惯,但笔者推荐 etcd。」

二、etcd 的 4 个核心机制

etcd 以 key-value 的形式进行数据的存储. 配合下面的这四种机制,使得 etcd 的应用场景更加的广泛.

  • 「Prefix 机制」:即前缀机制,也称「目录机制」,客户端向 etcd 放入 2 个键值对配置, 假如一个 key 是“/test/key1" , 另一个 key 是"/test/key2". 则通过前缀"/test"查询 etcd,返回一个列表包含 key 为“/test/key1" 和"/test/key2"的键值对数据;

  • 「Watch 机制」:即监听机制,watch 机制针对某个 key 进行监听,也支持针对前缀进行范围监听. 当被监听的 key 或前缀范围发生变化的时候,客户端会收到变更通知;

  • 「Lease 机制」:即租约机制(TTL,Time To Live),支持为 key-value 增加一个存活时间,超过这个时间 key-value 将过期被删除. 支持解约(删除 key-value),续约(增加 TTL 时间)等操作.

  • 「Revision 机制」:每个 key 带有一个 全局唯一的 Revision 号,每一次事务加 1,它是全局唯一的,所以通过 Revision 可以判定数据写操作的顺序,对于实现分布式锁和队列非常有帮助.

三、Leader 选举与客户端交互

使用 etcd 的时候,为了保证高可用,通常采用集群的部署方式。部署奇数个节点,通常建议是 3 个或 5 个,因为 etcd 集群之间需要「通过网络交互保证配置信息的一致性」。分布式多节点保证了高可用,但是节点太多了也不好,越多的节点网络消耗越大。至于为什么是奇数个?这就涉及到 Leader 选举的问题,奇数个方便投票出结果。

etcd 使用 raft 算法保证集群内各个节点之间数据一致性。raft 算法将集群内的节点分为 Leader, Follower, Candidate(候选人)这三个角色。

  • 集群初始化的时候,每个节点都是 Follower 角色。通过 raft 算法选举投票,选出一个节点作为 Leader。

  • Leader 作为主节点,与其他节点维持心跳,并同步数据至其他节点。

  • 当 Follower 一段时间内没有收到 leader 的心跳,就会将自己角色改为 Candidate 候选者,并发起一次新的选举,选举新的 Leader。

客户端在操作 etcd 集群数据的时候:

  • 读操作:客户端可以访问任意节点进行数据的读操作

  • 写操作:客户端访问任意节点进行写操作,如果该节点是 Follower,则将请求转发给 Leader。由 Leader 负责数据的写操作(增删改),将数据持久化,并向 Follower 发送同步数据的消息。

四、etcd 的应用场景

4.1. kubernetes 大脑

目前,etcd 的最典型的应用场景就是作为 Kubernetes 集群的大脑。

如果把 kubernetes 比作一个大饭店,那么 etcd 就是这个饭店的进销存+客户关系管理系统。

  • kubernetes 作为容器编排服务,将面向客户提供的各种服务进行合理的资源分配,服务编排。

  • 不可避免地,有一些 kubernetes 集群的配置和状态数据,例如 pod 的数量、它们的状态、命名空间等。需要有一个统一的记录、管理的地方,它就是 etcd。

最重要的是:「etcd 具备 watch 监听的功能,一旦某个配置或者某个状态发生变更,集群内所有的服务全都可以通过 watch 监听机制实时获取到消息,进而做出进一步的响应。」 几乎 etcd 的所有应用场景,都是基于 watch 监听机制产生的,包括我们后面为大家介绍的服务注册发现和订阅通知。

4.2. 服务注册与发现

其实 kubernetes 也利用 etcd 实现服务注册发现机制,但是上面的那张图不太好说明,我新画了两张图说明 etcd 在实现服务注册发现机制中的作用。

所谓的服务注册实现原理就是:服务在启动的时候,向 etcd 写入一条配置数据,该条配置数据说明自己的服务名称,服务 ip 地址,服务端口等信息。

所谓的服务发现实现原理举例:服务 C 的某个实例希望访问服务 A,服务 C 向 etcd 询问服务 A 的访问地址,etcd 响应结果:服务 A 有三个实例,地址列表如:xxx.xxx.xxx.xxx:端口yyy.yyy.yyy.yyy:端口zzz.zzz.zzz.zzz:端口。服务 C 不需要访问三个实例,访问其中一个就可以得到结果,所以它按照自己的负载均衡算法选了一个,这个就叫做:客户端负载均衡。

4.3. 健康检查与状态变更通知

衔接上文:「服务 C 下一次访问服务 A 的时候,还需要访问 etcd 么?答案是不需要」,它访问过一次之后,就会自己维护一个服务 A 访问地址的列表,「除非这个列表发生变化,否则是不会再次去询问 etcd 的。」那么一个服务怎么知道另一个服务的列表发生变化呢?比如:服务 A 的实例注册状态发生变化。可能是由于某种原因挂掉了,可能是 OOM 或者是网络问题等。

  • 服务在注册到 etcd 之后,会保存一个关于该服务的注册配置信息,该注册配置信息由一个 TTL,etcd 同时会与该服务维持心跳。一旦超过 TTL 时间,无法得到服务的心跳响应,etcd 就认为该节点的健康状态出现了问题,就会将该节点下线(注册配置信息删除)。

  • 服务在注册到 etcd 之后,会保持对 etcd 状态数据变更的监听,一旦获取监听结果:服务 A 的实例状态发生变更,该服务就会从 etcd 重新拉取服务 A 的注册列表。

4.4.分布式锁

跨进程跨系统的多线程操作公共资源,发生多线程竞争,为了避免线程不安全,需要使用分布式锁。如果多线程在单个进程内发生资源竞争,就是用 Lock 就可以了,不需要分布式锁。比如:你在 mysql 库里面有一个用户余额数据,多个进程内的线程同时更改这个值,可能发生并发的数据覆盖。为了避免这样的问题,多个进程排排队,A 先来,A 释放了锁 B 再来,B 释放了锁 C 再来。

举例:上图的 3 个 client 代表三个服务,都要操作某个资源数据。

  • 在尝试调用加锁 API 的时候,client1 获取到的 revision=1,它优先获得加锁的资格。加锁就是加一个带有 revision 的配置记录。其他的所有的服务,都通过 watch 机制监听锁的释放。

  • client 在尝试调用加锁 API 的时候,被分配了 revision。并且按照 revision 进行了排序,监听距离自己 revision 差值最小,而且小于自己的 Revision,不会产生惊群效应。

4.5.实现消息队列(纯扯淡)

我觉得使用 etcd 实现消息队列,是一种纯扯淡的做法。如果大家有什么异议,欢迎留言!

不是说做不了,确实写个 demo 是可以的。往 etcd 里面放数据,再通过 watch 机制进行监听,这不就是一个典型的消息队列么?扯淡!如果我只为了实现消息数据的发布订阅,其实有很多办法,我还用搭一个 etcd 集群?Spring 的 Event 机制,java 的响应式编程,哪怕自己搞一个 BlockQueue 呢,是不是都能实现消息的发布订阅。

我们之所以使用 kafka、RocketMQ 这样的消息队列,肯定是因为我们的异步数据达到一定的规模了。达到规模的异步消息数据传递根本就不是 etcd 的应用场景,正如本文开头所述:别忘了它叫做 etc 阿就 d,「它就是一个为分布式系统存储配置信息的,不是消息中间件。」

五、etcd 安装

本文为大家安装一个可以用于实验环境的 etcd 单机版,我们可以用它进行实验,后续我还会写文章介绍 etcd 集群的安装方式.下载 etcd 的安装包,访问github-etcd,我使用的是 linux 操作系统 64 位,所以下载的安装包是:etcd-v3.5.4-linux-amd64.tar.gz .如果网络条件不允许,可以搜索"etcd 国内下载加速",选择合适的下载安装包进行安装即可.

首先将安装包解压,解压之后 cd 进入安装目录,将 etcd 和 etcdctl 两个命令 copy 到/usr/local/bin/目录下面.

 
tar zxvf etcd-v3.5.4-linux-amd64.tar.gz;cd etcd-v3.5.4-linux-amd64;cp etcd etcdctl /usr/local/bin/;
 
 
 
复制代码
 

通过etcd --version命令查看 etcd 的版本,同时可以验证安装结果.如果不想敲全路径,可以把/usr/local/bin目录加入系统的 PATH 环境变量.

 
/usr/local/bin/etcd --version
 
 
 
复制代码
 

启动 etcd,这里的 listen-client-urls 和 advertise-client-urls 配置的作用是允许远程连接,0.0.0.0表示监听当前服务器的所有 ip, 监听端口是 2379. 假如你的服务器有多块网卡,多个固定 ip,你想指定 etcd 服务在某一个 ip 上提供服务,就可以用这个 ip 替换0.0.0.0

 
/usr/local/bin/etcd  --listen-client-urls 'http://0.0.0.0:2379' --advertise-client-urls 'http://0.0.0.0:2379'
 
 
 
复制代码
 

etcd 启动之后, 可以通过 etcdctl 命令向 etcd 中添加配置,如下所示使用 put 命令添加一个key=/dir1,value=aaa的键值对数据.可以使用 get 命令获取该配置信息.

 
# /usr/local/bin/etcdctl put /dir1 aaaOK# /usr/local/bin/etcdctl get /dir1/dir1aaa
 
 
 
复制代码
 

六、jetcd 的编码实现配置管理

下面为大家介绍通过 java API 的方式操作 etcd 的数据,首先通过 maven 的坐标引入 jetcd.我使用的版本相对比较旧,最新的版本已经是 0.7.8,不过我在使用的时候出现了与 netty 版本不一致的情况,报错:找不到 netty 相关的一些类.所以我就回退到 0.3.0 版本,使用方式上都是一样的.

 
<dependency>    <groupId>io.etcd</groupId>    <artifactId>jetcd-core</artifactId>    <version>0.3.0</version></dependency>
 
 
 
复制代码
 

下面的代码是使用 jetcd 操作 etcd 的配置数据,实现了数据的写操作,读操作,删除操作.详细用法看代码吧.下面的代码是 Junit 5 的单元测试用例的写法.

 
import io.etcd.jetcd.ByteSequence;import io.etcd.jetcd.Client;import io.etcd.jetcd.KV;import io.etcd.jetcd.kv.GetResponse;import io.etcd.jetcd.kv.PutResponse;import org.junit.jupiter.api.*;
import java.nio.charset.StandardCharsets;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;
import static junit.framework.TestCase.assertNotNull;
//这个注解配合函数的Order注解,决定测试用例函数的执行顺序@TestMethodOrder(MethodOrderer.OrderAnnotation.class)public class EtcdTest {  private static Client etcdClient;
  @BeforeAll  static void  init(){    etcdClient = Client.builder()             //这里的etcd服务列表可以写多个,用逗号分隔            .endpoints("http://192.168.161.3:2379".split(","))            .build();  }
  @Test  @Order(1)  @DisplayName("etcd写配置操作")  void putKV() throws ExecutionException, InterruptedException {    KV kv = etcdClient.getKVClient();    ByteSequence key = ByteSequence.from("key-str", StandardCharsets.UTF_8);    ByteSequence value = ByteSequence.from("value-str", StandardCharsets.UTF_8);    //put key-value配置信息    CompletableFuture<PutResponse> putRsp = kv.put(key,value);    assertNotNull(putRsp.get().getHeader());  }

  @Test  @Order(2)  @DisplayName("etcd读配置操作")  void getKV() throws ExecutionException, InterruptedException {    KV kv = etcdClient.getKVClient();    ByteSequence key = ByteSequence.from("key-str", StandardCharsets.UTF_8);    //通过key获取值    CompletableFuture<GetResponse> getRsp = kv.get(key);    String getBackValue = getRsp.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);    System.out.println("从etcd通过key获取value值为:" + getBackValue);  }

  @Test  @Order(3)  @DisplayName("删除配置操作")  void deleteKV() {    KV kv = etcdClient.getKVClient();    ByteSequence key = ByteSequence.from("key-str", StandardCharsets.UTF_8);    //通过key删除数据    kv.delete(key);  }}

 
 
 
复制代码
 

上面的代码只介绍了 etcd 的最基本的 key-value 操作,其实 etcd 客户端还提供了很多的 API,这些都将在我后续的文章中分布式锁,服务注册发现,配置变更监听,分布式系统 Leader 选举的内容中为大家介绍.

 
//租约Lease lease=etcdClient.getLeaseClient();//监听Watch watch =etcdClient.getWatchClient();//选举Election election =etcdClient.getElectionClient();//锁Lock lock=etcdClient.getLockClient();
 
 
 
复制代码
 
 
发布于: 2022-08-30阅读数: 2592

版权声明: 本文为 InfoQ 作者【字母哥哥】的原创文章。

原文链接:【https://xie.infoq.cn/article/3329de088beb60f5803855895】。未经作者许可,禁止转载。

[转帖]长篇图解 etcd 核心应用场景及编码实战的更多相关文章

  1. 长篇图解etcd核心应用场景及编码实战

    大家好啊,我是字母哥,今天写一篇关于etcd的文章,其实网上也有很多关于etcd的介绍,我就简明扼要,总结提炼,期望大家通过这一篇文章掌握etcd的核心知识以及编码技能! 本文首先用大白话给大家介绍一 ...

  2. 什么是ETCD及其应用场景

    ​ 源自公众号:BiggerBoy 一.什么是etcd? etcd 发音为/ˈɛtsiːdiː/,名字的由来,"distributed etc directory.",意思是&qu ...

  3. ETCD核心机制解析

    ETCD整体机制 etcd 是一个分布式的.可靠的 key-value 存储系统,它适用于存储分布式系统中的关键数据. etcd 集群中多个节点之间通过Raft算法完成分布式一致性协同,算法会选举出一 ...

  4. x264代码剖析(十五):核心算法之宏块编码中的变换编码

    x264代码剖析(十五):核心算法之宏块编码中的变换编码 为了进一步节省图像的传输码率.须要对图像进行压缩,通常採用变换编码及量化来消除图像中的相关性以降低图像编码的动态范围.本文主要介绍变换编码的相 ...

  5. 长篇图解java反射机制及其应用场景

    一.什么是java反射? 在java的面向对象编程过程中,通常我们需要先知道一个Class类,然后new 类名()方式来获取该类的对象.也就是说我们需要在写代码的时候(编译期或者编译期之前)就知道我们 ...

  6. 图解 K8s 核心概念和术语

    我第一次接触容器编排调度工具是 Docker 自家的 Docker Swarm,主要解决当时公司内部业务项目部署繁琐的问题,我记得当时项目实现容器化之后,花在项目部署运维的时间大大减少了,当时觉得这玩 ...

  7. Disruptor并发框架 (二)核心概念场景分析

    核心术语 RingBuffer(容器): 被看作Disruptor最主要的组件,然而从3.0开始RingBuffer仅仅负责存储和更新在Disruptor中流通的数据.对一些特殊的使用场景能够被用户( ...

  8. Cocos2d-x 核心概念 - 场景(Scene)

    场景(Scene):构成游戏的界面 场景类的几种: 展示类场景 选项类场景 游戏场景 场景类继承自Node类

  9. [转帖]hdfs hbase hive hbase适用场景

    hdfs hbase hive hbase适用场景 https://www.cnblogs.com/liyulong1982/p/6001822.html Hive 不想用程序语言开发MapReduc ...

  10. [转帖]Zookeeper vs etcd vs Consul比较

    Zookeeper vs etcd vs Consul比较 https://it.baiked.com/consul/2341.html 需要转型 加强学习. 如果使用预定义的端口,服务越多,发生冲突 ...

随机推荐

  1. flutter常用的设计模式

    单例模式(Singleton Pattern): 确保一个类只有一个实例,并提供一个全局访问点. 工厂模式(Factory Pattern): 定义一个创建对象的接口,但让子类决定具体实例化哪个类:常 ...

  2. 如何使用loki查询日志中大于某一数字的值的日志

    简介 loki是一款轻量级的日志收集中间件,比elk体系占用的内存更小,采用go语言开发,可以利用grafana来查询loki中存储的日志,loki存储日志只对提前预设的标签做索引,所以日志存储空间占 ...

  3. 一文让你彻底掌握ThreadLocal

    本文分享自华为云社区<[高并发]一文带你彻底搞懂ThreadLocal>,作者: 冰 河. 我们都知道,在多线程环境下访问同一个共享变量,可能会出现线程安全的问题,为了保证线程安全,我们往 ...

  4. 构建万物互联,华为云IoT+鸿蒙重燃物体感知

    摘要:鸿蒙的出现,让硬件.软件行业面临着变革与重构的洪流,但激流勇进中,也潜藏着巨大机遇.物联网设备与鸿蒙结合成为必然趋势,本文将解读华为云IoT+鸿蒙如何强强联合,为物联网行业提供新的思路和方法. ...

  5. Mock服务设计与实现:MySQL驱动字节码修改增强

    摘要:华为导流测试平台通过对线上流量回放到被测环境中,利用线上真实流量进行充分测试,保证业务系统稳定上线.但是业务在导流测试过程中现网数据库往往难以同步到测试环境,导致现网数据无法正常回放,测试价值降 ...

  6. 为AR&VR黑科技:以“自由视角”360度尽展舞台唯美

    摘要:看华为的黑科技,如何用"自由视角"让观众感受舞台"风暴"的魅力所在. "风暴"降临 2021年1月9日晚上,我坐在电视机前,等待湖南卫 ...

  7. Python图像处理丨图像的灰度线性变换

    摘要:本文主要讲解灰度线性变换. 本文分享自华为云社区<[Python图像处理] 十五.图像的灰度线性变换>,作者:eastmount. 一.图像灰度线性变换原理 图像的灰度线性变换是通过 ...

  8. CVE-2022-22965 漏洞分析,安全问题早发现

    摘要:Spring在进行参数绑定时调用的 BeanWrapperImpl在进行JavaBean操作时触发了此漏洞. 本文分享自华为云社区<CVE-2022-22965 漏洞分析>,作者:X ...

  9. 火山引擎 DataLeap 构建Data Catalog系统的实践(二):技术与产品概览

    技术与产品概览 架构设计 元数据的接入 元数据接入支持T+1和近实时两种方式 上游系统:包括各类存储系统(比如Hive. Clickhouse等)和业务系统(比如数据开发平台.数据质量平台等) 中间层 ...

  10. Solon2 之基础:四、应用启动过程与完整生命周期

    串行的处理过程(含六个事件扩展点 + 两个函数扩展点),代码直接.没有什么模式.易明 提醒: 启动过程完成后,项目才能正常运行(启动过程中,不能把线程卡死了) AppBeanLoadEndEvent ...