大家好啊,我是字母哥,今天写一篇关于etcd的文章,其实网上也有很多关于etcd的介绍,我就简明扼要,总结提炼,期望大家通过这一篇文章掌握etcd的核心知识以及编码技能!
本文旨在帮助大家理解etcd,从宏观角度俯瞰etcd全局,掌握etcd的基本操作技能。后续我还会写一个系列的文章,将每一种应用场景代码化,期待大家关注我和我的公众号:字母哥杂谈。后续计划章节内容如下:
用过linux的朋友请举手,好的,我看见了! 在 linux 中所有自动安装的系统软件配置文件都存储在一个名为/etc的目录中。“d”表示distributed分布式,etcd为分布式模型,所以etcd的核心应用场景是:分布式系统的配置信息存储。
网上很多文章上来第一句话照搬英文官网:etcd 是一个高度一致的分布式键值存储系统。很多朋友看完就问了,这玩意和redis有啥区别? 笔者要说,真的不要这么比,etcd从名字上就已经告诉你了,它是存储配置信息(元数据)的。和redis在架构应用上就不在一个层面,它对标的产品应该是zookeeper。虽然zookeeper在很多java的分布式系统的应用中比较广泛,但是etcd作为后起之秀,乘kubernetes的东风,大有超越zookeeper的趋势。
大部分功能和zookeeper都是一样的,目前看java程序员用zookeeper的更多,其他程序员用etcd更多。都是基于习惯,但笔者推荐etcd。
etcd以key-value的形式进行数据的存储. 配合下面的这四种机制,使得etcd的应用场景更加的广泛.
使用etcd的时候,为了保证高可用,通常采用集群的部署方式。部署奇数个节点,通常建议是3个或5个,因为etcd集群之间需要通过网络交互保证配置信息的一致性。分布式多节点保证了高可用,但是节点太多了也不好,越多的节点网络消耗越大。至于为什么是奇数个?这就涉及到Leader选举的问题,奇数个方便投票出结果。

etcd使用raft算法保证集群内各个节点之间数据一致性。raft算法将集群内的节点分为Leader, Follower, Candidate(候选人)这三个角色。
客户端在操作etcd集群数据的时候:
目前,etcd的最典型的应用场景就是作为Kubernetes 集群的大脑。

如果把kubernetes比作一个大饭店,那么etcd就是这个饭店的进销存+客户关系管理系统。
最重要的是:etcd具备watch监听的功能,一旦某个配置或者某个状态发生变更,集群内所有的服务全都可以通过watch监听机制实时获取到消息,进而做出进一步的响应。 几乎etcd的所有应用场景,都是基于watch监听机制产生的,包括我们后面为大家介绍的服务注册发现和订阅通知。
其实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不需要访问三个实例,访问其中一个就可以得到结果,所以它按照自己的负载均衡算法选了一个,这个就叫做:客户端负载均衡。
衔接上文:服务C下一次访问服务A的时候,还需要访问etcd么?答案是不需要,它访问过一次之后,就会自己维护一个服务A访问地址的列表,除非这个列表发生变化,否则是不会再次去询问etcd的。
那么一个服务怎么知道另一个服务的列表发生变化呢?比如:服务A的实例注册状态发生变化。可能是由于某种原因挂掉了,可能是OOM或者是网络问题等。

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

举例:上图的3个client代表三个服务,都要操作某个资源数据。
我觉得使用etcd实现消息队列,是一种纯扯淡的做法。如果大家有什么异议,欢迎留言!
不是说做不了,确实写个demo是可以的。往etcd里面放数据,再通过watch机制进行监听,这不就是一个典型的消息队列么?扯淡!如果我只为了实现消息数据的发布订阅,其实有很多办法,我还用搭一个etcd集群?Spring的Event机制,java的响应式编程,哪怕自己搞一个BlockQueue呢,是不是都能实现消息的发布订阅。
我们之所以使用kafka、RocketMQ这样的消息队列,肯定是因为我们的异步数据达到一定的规模了。达到规模的异步消息数据传递根本就不是etcd的应用场景,正如本文开头所述:别忘了它叫做etc阿就d,它就是一个为分布式系统存储配置信息的,不是消息中间件。
本文为大家安装一个可以用于实验环境的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 aaa
OK
# /usr/local/bin/etcdctl get /dir1
/dir1
aaa
下面为大家介绍通过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
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我正在使用ruby1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD