大家好啊,我是字母哥,明天写一篇对于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();
欢送关注我的布告号:字母哥杂谈,回复003赠送作者专栏《docker修炼之道》的PDF版本,30余篇精品docker文章。字母哥博客:zimug.com